<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Diego Ponce de León]]></title><description><![CDATA[Diego Ponce de León]]></description><link>https://xleon.net</link><generator>RSS for Node</generator><lastBuildDate>Fri, 10 Apr 2026 21:02:24 GMT</lastBuildDate><atom:link href="https://xleon.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Dart code generation files in git. Yes or no?]]></title><description><![CDATA[I started writing Flutter and Dart code a few weeks ago for a real production project that we are migrating from another tech stack (Xamarin).
Soon I started using the power of code generation libraries like freezed, json_serializable, slang_build_ru...]]></description><link>https://xleon.net/dart-code-generation-files-in-git-yes-or-no</link><guid isPermaLink="true">https://xleon.net/dart-code-generation-files-in-git-yes-or-no</guid><category><![CDATA[Dart]]></category><category><![CDATA[codegeneration]]></category><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Mon, 30 Oct 2023 00:35:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698623417616/73b0db97-0db6-4825-b052-053cf93fc304.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I started writing Flutter and Dart code a few weeks ago for a real production project that we are migrating from another tech stack (Xamarin).</p>
<p>Soon I started using the power of code generation libraries like <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a>, <a target="_blank" href="https://pub.dev/packages/json_serializable">json_serializable</a>, <a target="_blank" href="https://pub.dev/packages/slang_build_runner">slang_build_runner</a> and others. These libraries usually work with attributes in your class or methods, or by transforming existing metadata files into more useful stuff.</p>
<p>The <a target="_blank" href="https://pub.dev/packages/build_runner">build_runner</a> package will be in charge of generating all the code for you. I learned this is a very common pattern and practice in the Dart world and hard to avoid if you value your time.</p>
<p>Have you wondered if those files should go into the git repository or not?</p>
<p>I asked myself that question. Searching some people's opinions online I saw there are different points of view on the subject so I needed to figure it out internally on my team.</p>
<p>Next, let's explore reasons to include or exclude those files 👇.</p>
<h3 id="heading-the-main-reasons-to-include-code-generation-files">The main reasons to include code generation files</h3>
<p>If the files are not included, your app will not build correctly whenever you:</p>
<ul>
<li><p>clone the project</p>
</li>
<li><p>switch branches</p>
</li>
<li><p><code>git clean -xdf</code></p>
</li>
<li><p>other cleaning scenarios</p>
</li>
</ul>
<p>It's inconvenient to manually run commands or tasks to generate all files every time.</p>
<p>I heard some people saying that code generation can differ from one machine to another. For instance: your local machine vs a CI server machine. However, I guess this can be done right by taking good care of dependency versions.</p>
<h3 id="heading-reasons-to-exclude-code-generation-files">Reasons to exclude code generation files</h3>
<p>Let's assume you work in a team environment where Pull Requests and code reviews are common practice. The list of files to review will increase significantly when all the generated files are there, making it harder for your teammates (we can learn to ignore them but it requires an extra effort that might not be worth it).</p>
<p>Besides that, you will be polluting your git repository with unnecessary trash that eventually will increase the size big time. It might not look like a problem now, but your build pipeline will suffer in the long term.</p>
<p>If you work alone in small projects though, those files should not be a problem.</p>
<h3 id="heading-guess-what-we-chose-to-exclude-the-files">Guess what? we chose to exclude the files</h3>
<p>But then we needed to fix the problems mentioned above.<br />It became easy with a couple of workarounds.</p>
<p>The first thing to notice is that watching files to re-generate the code is as simple as running this command line:</p>
<pre><code class="lang-bash">flutter pub run build_runner watch --delete-conflicting-outputs
</code></pre>
<p>If you feel too lazy to mess with your terminal you might like this <a target="_blank" href="https://github.com/gaetschwartz/build-runner">VSCode extension</a> which does the same with a click of a button.</p>
<p>But that didn't always work for me.<br />If someone added a new dart package to Pubspec we had to run <code>dart pub get</code>.</p>
<p>The library we use for translations (<a target="_blank" href="https://pub.dev/packages/slang">slang</a>) requires an additional command to convert JSON files into dart code: <code>dart run slang</code>.</p>
<p>And who knows what we'll need in the future?</p>
<h3 id="heading-simple-solution-automate-the-boring-stuff">Simple solution: automate the boring stuff</h3>
<p>Simple as a git post-checkout script.<br />You can tell Git to run any command(s) you like when switching branches.<br />Here's how to:</p>
<p>Create a file <code>post-checkout</code> (without extension) at: <code>[Project root]/.git/hooks/</code> directory.</p>
<p>Your system may be hiding the <code>.git</code> directory, but this can be done easily with your command line:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your project
touch .git/hooks/post-checkout <span class="hljs-comment"># creates the file</span>
chmod -x .git/hooks/post-checkout <span class="hljs-comment"># give it execute permissions</span>
code .git/hooks/post-checkout <span class="hljs-comment"># open the file with vscode for edit</span>
</code></pre>
<p>At this point, you can add any commands to the file. This is my current setup:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># 👆this line here is important!</span>

<span class="hljs-comment"># restore packages</span>
dart pub get 
<span class="hljs-comment"># generate translations</span>
dart run slang 
<span class="hljs-comment"># library code generation</span>
dart run build_runner build --delete-conflicting-outputs
</code></pre>
<p>And now I can happily switch to any branch of the project and everything will just work.</p>
<p>The only downside now is that switching branches will take longer than expected.</p>
<blockquote>
<p>If you like this solution and want to share it with the team, beware that<br />git hooks cannot be shared through git (as with any other file inside <code>.git/</code>) so your teammates won't see it. Instead, you can instruct them how to set it up or just add this information to the project's README as I did.</p>
<p>There is a workaround though, but that goes beyond the scope of this post. Here's a <a target="_blank" href="https://dev.to/florianbaba/a-githooks-example-and-how-to-share-it-with-the-team-42i0">blog explaining it</a>.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[How to use Charles Proxy in Xamarin to capture network traffic (including SSL)]]></title><description><![CDATA[Capturing network traffic between your application and your server is a handy way of checking and debugging the data you are sending and what comes back from the server. Charles Proxy shows network calls in a sequence or as a tree structure, includin...]]></description><link>https://xleon.net/how-to-use-charles-proxy-in-xamarin-to-capture-network-traffic-including-ssl</link><guid isPermaLink="true">https://xleon.net/how-to-use-charles-proxy-in-xamarin-to-capture-network-traffic-including-ssl</guid><category><![CDATA[proxy]]></category><category><![CDATA[Xamarin]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Fri, 16 Nov 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/40XgDxBfYXM/upload/bea250acafb2489f8da8b6fe70920c11.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Capturing network traffic between your application and your server is a handy way of checking and debugging the data you are sending and what comes back from the server. <a target="_blank" href="https://www.charlesproxy.com/">Charles Proxy</a> shows network calls in a sequence or as a tree structure, including headers and all kinds of HTTP information like response times and payload size. It even allows you to simulate adverse network conditions and throttling, ie: an unstable 3g connection.</p>
<p>I´ve been doing this forever on web development so I tried on mobile by configuring a proxy on Android emulators according to some detailed tutorials out there.</p>
<blockquote>
<p>Result: None of the tutorials worked with my Xamarin app</p>
</blockquote>
<p>Well, those tutorials work 😜 but Xamarin needs an additional piece of code. As I don´t want to replicate wonderful posts from other developers, I recommend you follow this specific tutorial step by step: <a target="_blank" href="https://medium.com/@daptronic/the-android-emulator-and-charles-proxy-a-love-story-595c23484e02">The Android Emulator and Charles Proxy: A Love Story</a>. It´s a bit long but hey, you only need to do it once!</p>
<h2 id="heading-xamarin-specific-bits-writing-a-custom-httpclienthandler">Xamarin-specific bits: writing a custom HttpClientHandler</h2>
<p>I´ve been pulling my hair big time because the tutorial didn´t work until I tried manually pointing to Charles Proxy from the app code.</p>
<p>First of all, check your local IP and Charles port.<br />On Charles, click menu &gt; Local IP addresses:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698362544750/7beace14-b360-4a68-ab57-84b624f7eb8e.png" alt class="image--center mx-auto" /></p>
<p>Then go to menu &gt; Proxy &gt; Proxy settings… to check the default port:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698362556602/344ea708-1c5c-42ea-b751-7e7394073e1b.png" alt class="image--center mx-auto" /></p>
<p><img src="/images/charles-port.png" alt /></p>
<p>Setup the proxy with a custom <code>HttpClientHandler</code>:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> handler = <span class="hljs-keyword">new</span> HttpClientHandler
{
    <span class="hljs-comment">// local ip, charles port</span>
    Proxy = <span class="hljs-keyword">new</span> WebProxy(<span class="hljs-string">"192.168.0.52"</span>, <span class="hljs-number">8888</span>) 
};
</code></pre>
<p>Then use it whenever you create an <code>HttpClient</code> in your app</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> client = <span class="hljs-keyword">new</span> HttpClient(handler);
<span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> make some requests to test charles!</span>
</code></pre>
<p>At this point, everything should work and you should be able to observe all network traffic. For the sake of your mental health, make sure you only do this in debug mode and it never gets to production.</p>
<h2 id="heading-bonus">Bonus</h2>
<p><strong>Beware</strong> that your app stops working if your <code>HttpClientHandler</code> is pointing to Charles but Charles is not running. To tackle this issue I use an environment variable that I can set to <code>false</code> when I´m not debugging network traffic:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// http proxy</span>
<span class="hljs-keyword">if</span> (Environment.GetEnvironmentVariable(<span class="hljs-string">"UseHttpProxy"</span>) == <span class="hljs-string">"true"</span>)
{
    <span class="hljs-keyword">var</span> handler = <span class="hljs-keyword">new</span> HttpClientHandler
    {
        Proxy = <span class="hljs-keyword">new</span> WebProxy(
            Environment.GetEnvironmentVariable(<span class="hljs-string">"ProxyHost"</span>), 
            <span class="hljs-keyword">int</span>.Parse(Environment.GetEnvironmentVariable(<span class="hljs-string">"ProxyPort"</span>) 
                ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException()))
    };

    _client.SetHandler(handler);
}
</code></pre>
<p>To set env variables on Android, you have to create a text file in your Android project root, call it <em>env.txt</em> (it can be any name) and set its <em>Build Action</em> to <code>AndroidEnvironment</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698362632201/f513288f-7709-421b-8bb7-b878bc85df98.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Fixing push notifications on Android 8. Aka channels]]></title><description><![CDATA[Android 8 (API level 26) expects any notification to use a Channel. So we need to implement them but only when the API level is 26+. Channels should be ignored otherwise because they won´t be available, meaning that your app will throw an exception i...]]></description><link>https://xleon.net/fixing-push-notifications-on-android-8-aka-channels</link><guid isPermaLink="true">https://xleon.net/fixing-push-notifications-on-android-8-aka-channels</guid><category><![CDATA[push notifications]]></category><category><![CDATA[Android]]></category><category><![CDATA[Xamarin]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Sun, 19 Aug 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698363059750/3f8ab8fb-f593-42de-b46a-7ef785f30785.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Android 8 (API level 26) expects any notification to use a <a target="_blank" href="https://developer.android.com/training/notify-user/channels">Channel</a>. So we need to implement them but only when the API level is 26+. Channels should be ignored otherwise because they won´t be available, meaning that your app will throw an exception if you try to use them.</p>
<p>We will create a default channel to be used when any push notification arrives. You can create multiple channels but I bet most applications will be just fine with one.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">NotificationHelper</span> 
{ 
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> ChannelId = <span class="hljs-string">"com.unique.string.onyourapp.default"</span>;

    <span class="hljs-comment">// make sure you call this method only once </span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">CreateDefaultChannel</span>(<span class="hljs-params">Context context</span>)</span> 
    { 
        <span class="hljs-keyword">if</span> (Build.VERSION.SdkInt &lt; BuildVersionCodes.O) 
            <span class="hljs-keyword">return</span>;
        <span class="hljs-keyword">var</span> channel = <span class="hljs-keyword">new</span> NotificationChannel(ChannelId, 
            <span class="hljs-string">"AppName default"</span>, NotificationImportance.High);

        channel.EnableVibration(<span class="hljs-literal">true</span>); 
        channel.LockscreenVisibility = NotificationVisibility.Public;

        <span class="hljs-keyword">var</span> notificationManager = (NotificationManager) context
            .GetSystemService(Context.NotificationService);

        notificationManager.CreateNotificationChannel(channel); 
    }

    <span class="hljs-comment">// method extension to ease the creation of the nofication </span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> NotificationCompat.<span class="hljs-function">Builder <span class="hljs-title">SetChannelIdIfNeeded</span>(<span class="hljs-params"> 
        <span class="hljs-keyword">this</span> NotificationCompat.Builder builder, <span class="hljs-keyword">string</span> channelId</span>)</span> 
    { 
        <span class="hljs-keyword">if</span>(Build.VERSION.SdkInt &gt;= BuildVersionCodes.O) 
            builder.SetChannelId(channelId);

        <span class="hljs-keyword">return</span> builder; 
    } 
}
</code></pre>
<p>It's a good idea to create the default channel at the app start, either on your main activity or your custom Application <code>OnCreate()</code> override:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnCreate</span>(<span class="hljs-params"></span>)</span> 
{ 
    <span class="hljs-keyword">base</span>.OnCreate(); 
    NotificationHelper.CreateDefaultChannel(<span class="hljs-keyword">this</span>); 
}
</code></pre>
<p>Last, but not least, you need to make and slight modification to the code that creates the notification.</p>
<p>Before:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> notificationBuilder = <span class="hljs-keyword">new</span> NotificationCompat.Builder(<span class="hljs-keyword">this</span>)
    .SetSmallIcon(Resource.Drawable.ic_notification)
    .SetContentTitle(title)
    .SetContentText(message)
    .SetAutoCancel(<span class="hljs-literal">true</span>)
    .SetContentIntent(pendingIntent);
</code></pre>
<p>After:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> notificationBuilder = <span class="hljs-keyword">new</span> NotificationCompat.Builder(<span class="hljs-keyword">this</span>)
    .SetChannelIdIfNeeded(NotificationHelper.ChannelId) <span class="hljs-comment">// method extension</span>
    .SetSmallIcon (Resource.Drawable.ic_notification)
    .SetContentTitle(title)
    .SetContentText(message)
    .SetAutoCancel(<span class="hljs-literal">true</span>)
    .SetContentIntent(pendingIntent);
</code></pre>
<p>From now on your push notifications will start working again on Android 8.</p>
]]></content:encoded></item><item><title><![CDATA[Android 8 adaptative (vector) icons]]></title><description><![CDATA[Once we change the target Android version to 26 or higher, the app icon suddenly gets messed up, from a full-size icon to a tiny, hard to notice thing inside a white shape (a circle on the default Google launcher). I delayed upgrading to 26+ because ...]]></description><link>https://xleon.net/android-8-adaptative-vector-icons</link><guid isPermaLink="true">https://xleon.net/android-8-adaptative-vector-icons</guid><category><![CDATA[Xamarin]]></category><category><![CDATA[Android]]></category><category><![CDATA[app-icon]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Sun, 19 Aug 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698365731741/3f32daee-72c1-4a50-bef4-927f1e1de3e2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Once we change the target Android version to 26 or higher, the app icon suddenly gets messed up, from a full-size icon to a tiny, hard to notice thing inside a white shape (a circle on the default Google launcher). I delayed upgrading to 26+ because of this, and also beacuse I didn´t understand how the new adaptative icons work. But it´s about time, so here we go.</p>
<h3 id="heading-how-do-i-create-it">How do I create it?</h3>
<p>I´m assuming your launcher icon (app icon) is called <code>ic_launcher</code>, but it can be any name. Just make sure it´s the same name you set on your main launcher activity:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Activity(
        Label = <span class="hljs-meta-string">"YourApp"</span>
        , MainLauncher = true
        , Icon = <span class="hljs-meta-string">"@mipmap/ic_launcher"</span></span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">YourInitialActivity</span> : <span class="hljs-title">AppCompatActivity</span> {}
</code></pre>
<p>Create the folder <code>YourAndroidProject/Resources/mipmap-anydpi-26</code>.</p>
<p>Inside that folder create an xml file with the exact name of your launcher icon. For instance: <code>ic_launcher.xml</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698365774409/df7b2f9c-b444-491c-bc31-3258ecea0653.png" alt class="image--center mx-auto" /></p>
<p><img src="/images/adaptative-icon-file-structure.png" alt /></p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span>
<span class="hljs-comment">&lt;!-- ic_launcher.xml content --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">adaptive-icon</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">background</span> <span class="hljs-attr">android:drawable</span>=<span class="hljs-string">"@color/ic_launcher_background_color"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">foreground</span> <span class="hljs-attr">android:drawable</span>=<span class="hljs-string">"@mipmap/foreground"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">adaptive-icon</span>&gt;</span>
</code></pre>
<p>Replace <code>ic_launcher_background_color</code> with any color you like.</p>
<p>Now create an SVG file with the vector tool of your choice, like Sketch, Illustrator, Inkscape, etc. The canvas should be a square of 108 x 108. However the size doesn´t matter as we can change it later on the XML, but it´s handy to set it right for design purposes. For that, I would suggest you download a template from the <a target="_blank" href="https://medium.com/google-design/designing-adaptive-icons-515af294c783">link</a> (Resources and tools). I used the Illustrator template and placed my icon inside the grey circle in the middle.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698365866346/c3edd1bb-9d17-4b81-b57f-eac8aa261ef6.png" alt class="image--center mx-auto" /></p>
<p>Here you can see the same with the template grid:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698365876306/abb07d8f-c651-4afa-b72b-423702d81252.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-exporting-the-svg">Exporting the SVG:</h3>
<p>This will be the <em>foreground</em> graphic of your adaptative icon, so apart from the icon, everything else should be hidden, BUT YOU NEED an square shape for the icon to size correctly. The trick is to make that shape invisible, setting the alpha to 0, as per the screenshot above.</p>
<p>Export it to SVG, and then <a target="_blank" href="http://inloop.github.io/svg2android/">convert it to an Android vector XML file</a>. Call that file <code>foreground.xml</code> and put it in the <code>mipmap-anydpi-26</code> folder.</p>
<p>Now open <code>foreground.xml</code> and make sure the width and height are 108:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">vector</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">android:width</span>=<span class="hljs-string">"108dp"</span>
    <span class="hljs-attr">android:height</span>=<span class="hljs-string">"108dp"</span>
    <span class="hljs-attr">android:viewportWidth</span>=<span class="hljs-string">"108"</span>
    <span class="hljs-attr">android:viewportHeight</span>=<span class="hljs-string">"108"</span>&gt;</span>

    ...

<span class="hljs-tag">&lt;/<span class="hljs-name">vector</span>&gt;</span>
</code></pre>
<p>That´s it.</p>
<h3 id="heading-tldr">TL;DR</h3>
<p>Take a look at the <a target="_blank" href="https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive">Official UI guideline</a>, a blog post about <a target="_blank" href="https://medium.com/google-design/understanding-android-adaptive-icons-cee8a9de93e2">Understanding Android Adaptive Icons</a> and another about <a target="_blank" href="https://medium.com/google-developers/implementing-adaptive-icons-1e4d1795470e">Implementing Adaptive Icons</a></p>
<p>IMO, the most remarkable improvement is that the launcher icon can now be an Android vector XML. That means you can take any SVG file, convert it to the Android XML format with a <a target="_blank" href="http://inloop.github.io/svg2android/">tool</a>, and then use that single file in all 26+ devices.</p>
<h3 id="heading-can-i-get-rid-of-the-former-drawablemipmapdensity-versions">Can I get rid of the former <code>drawable/mipmap_[density]</code> versions?</h3>
<p>NO. Any device using an SDK less than 26 will ignore your new adaptative icon, so you still have to provide images for the different pixel densities for those devices.</p>
<h3 id="heading-so-whats-the-point-then">So what´s the point then?</h3>
<ol>
<li><p>As I said earlier, your icon will look bad on API 26+ without an adaptative icon.</p>
</li>
<li><p>Device OEMs can now consolidate the appearance of all icons on the launcher. This means that your icon can now <a target="_blank" href="https://adapticon.tooo.io/#/bg=https://i.imgur.com/iqGpQCh.png/fg=https://i.imgur.com/jceH9gr.png">adapt to different kinds of shape masks, and even be animated</a>, looking good everywhere. One device OEM can set a circle mask on them, while another OEM can use a rectangle with rounded corners.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[UIStackView magic]]></title><description><![CDATA[This component is one of the things that should´ve been there for ages, as it facilitates layout design considerably. In every UI environment, there is a basic need to stack elements horizontally or vertically, having them rearranged when any element...]]></description><link>https://xleon.net/uistackview-magic</link><guid isPermaLink="true">https://xleon.net/uistackview-magic</guid><category><![CDATA[Xamarin]]></category><category><![CDATA[iOS]]></category><category><![CDATA[UIStackView]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Tue, 11 Jul 2017 19:44:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366136455/6e9bcd54-589a-4aac-aaba-79d5a0fec098.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This component is one of the things that should´ve been there for ages, as it facilitates layout design considerably. In every UI environment, there is a basic need to stack elements horizontally or vertically, having them rearranged when any element is added or removed. This takes a second in other platforms however iOS auto-layout is sometimes a painful process for pretty simple tasks.</p>
<h2 id="heading-whats-wrong-with-the-good-old-super-awesome-autolayout">What´s wrong with the good old, super awesome AutoLayout?</h2>
<p>Consider the following layout (“–” represents padding):</p>
<p><code>|--view1--view2--view3--|</code></p>
<p>Doing this silly layout the “old” way involves lots of constraints, for instance:</p>
<ul>
<li><p>align the views vertically (2 or 3 constraints)</p>
</li>
<li><p>set leading and trailing of each view (3 to 4 constraints)</p>
</li>
<li><p>set with and height of each view (6 constraints or maybe 3 if they provide intrinsic height)</p>
</li>
</ul>
<p>Next week your boss comes to tell you that view2 is no longer needed, so you have to:</p>
<ul>
<li><p>delete view2</p>
</li>
<li><p>manually rearrange view3 to get the place where view2 was placed, fixing all the wrong constraints</p>
</li>
</ul>
<p>Then, a great designer in your team tells you by Slack that paddings are too small. There´s no single “padding” or “margin” property. Again, you need to change all leading/trailing constraints of each view, one by one.</p>
<p>And well… we are talking about “design time” here (aka Interface Builder). If you need to do this on runtime (<strong>state changes</strong>) it may become a nightmare because you first need to locate the attached constraints to remove them and then create new ones, if you know what I mean.</p>
<p>Now imagine you´ve got like 5 more places in the screen where this can happen. The next move is probably pulling your hair and complaining about how miserable your life is.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698359207936/95b52d14-467a-439c-941c-d4f75115fba2.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-what-can-uistackview-do-for-me">What can UIStackView do for me?</h2>
<p>This guy is using AutoLayout internally and it does nothing you can´t do with AutoLayout and some code but it will make your life easier in many ways. Think of it as some kind of Android LinearLayout. It can also be nested easily to achieve more complex layouts.</p>
<p>Going back to the previous example, if view1, view2 and view3 were within a <code>UIViewStack</code> wouldn´t need any constraint created manually (unless you want to) and removing one of them (design time or runtime) will readjust all the constraints for you. <strong>Done</strong>. The same happens if you want to add a new view to the stack. You can even animate that change with a one-liner, as we will see later.</p>
<p>To recap:</p>
<ol>
<li><p>Design time layouts become easier</p>
</li>
<li><p>Coded layouts become easier</p>
</li>
<li><p>Runtime state changes (adding or removing elements) take a single line of code.</p>
</li>
<li><p>Animating layout changes is now very simple</p>
</li>
</ol>
<h2 id="heading-shut-up-and-show-me-the-code">Shut up and show me the code</h2>
<p>No need to give you all the small details of this component, as you can easily <a target="_blank" href="https://www.google.es/search?q=uistackview+tutorial&amp;oq=uistackview+tutorial&amp;aqs=chrome..69i57j0l3j69i60.3412j0j4&amp;sourceid=chrome&amp;ie=UTF-8">google it</a>. Instead, I´m going to jump right to the code with some real cases I came up with.</p>
<p>All the samples will be coded (sorry IB fans) and layout with the anchor constraint API (did I say it correctly?) and a few <a target="_blank" href="https://github.com/xleon/UIStackViewPlayground/blob/master/Helpers/ConstraintUtil.cs">method extensions</a> to speed up trivial tasks. If you don´t know what it is, go ahead and read my <a target="_blank" href="http://xleon.net/xamarin/ios/nslayoutconstraint/autolayout/getting-fancy-with-uiview-anchors-and-state-changes.html">previous post</a>.</p>
<h2 id="heading-adding-and-removing-elements">Adding and removing elements</h2>
<p>For <code>UIStackView</code> to manage children's constraints properly, use any of the following methods:</p>
<pre><code class="lang-csharp">stackView.AddArrangedSubview(view);
stackView.InsertArrangedSubview(view, index);
stackView.RemoveArrangedSubview(view);
</code></pre>
<p>You can also remove a child view like you normally would:</p>
<p><code>view.RemoveFromSuperview();</code></p>
<h2 id="heading-a-vertical-scroll-of-stacked-elements">A vertical scroll of stacked elements</h2>
<p>Let´s say you need a simple scroll for a small number of stacked elements (better use a Table if you need many) but you don´t need/want to mess with cells.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366326501/b689e0dc-f266-4857-96ce-60539832351f.gif" alt class="image--center mx-auto" /></p>
<p>A simple <code>UIStackView</code> within a <code>UIScrollView</code> will make the trick:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ViewDidLoad</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">base</span>.ViewDidLoad();

    <span class="hljs-comment">// Create and add Views</span>

    <span class="hljs-keyword">var</span> stackView = <span class="hljs-keyword">new</span> UIStackView
    {
        Axis = UILayoutConstraintAxis.Vertical,
        <span class="hljs-comment">// make the inner views to fill the whole available width</span>
        Alignment = UIStackViewAlignment.Fill, 
        Distribution = UIStackViewDistribution.EqualSpacing,
        <span class="hljs-comment">// margin between views</span>
        Spacing = <span class="hljs-number">4</span>, 
        <span class="hljs-comment">// apply padding between the parent view (the scroll) </span>
        <span class="hljs-comment">// and the stack</span>
        LayoutMargins = <span class="hljs-keyword">new</span> UIEdgeInsets(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>),
        <span class="hljs-comment">// this will make padding actually work</span>
        LayoutMarginsRelativeArrangement = <span class="hljs-literal">true</span> 
    };

    <span class="hljs-keyword">var</span> scroll = <span class="hljs-keyword">new</span> UIScrollView();
    Add(scroll);

    scroll.AddSubview(stackView);

    <span class="hljs-comment">// Add some views to the stack with random height and color</span>
    <span class="hljs-comment">// Notice that we use AddArrangedSubview rather than the </span>
    <span class="hljs-comment">// former AddSubview for UIStackView to manage it. </span>
    <span class="hljs-comment">// Otherwise it won´t work</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">20</span>; i++)
        stackView.AddArrangedSubview(GetRandomView());

    <span class="hljs-comment">// Layout Views</span>

    <span class="hljs-comment">// For AutoLayout to work, all views and nested views need to set </span>
    <span class="hljs-comment">// "TranslatesAutoresizingMaskIntoConstraints" property to `false`. </span>
    <span class="hljs-comment">// It´s easy to forget it so I created an extension method that </span>
    <span class="hljs-comment">// will do this to the view and its subviews</span>
    scroll.EnableAutoLayout();

    <span class="hljs-comment">// "FullSizeOf" is a method extension to set leading, trailing, </span>
    <span class="hljs-comment">// bottom and top constraints</span>
    scroll.FullSizeOf(View); 
    stackView.FullSizeOf(scroll);

    <span class="hljs-comment">// constraint needed when the UIStackView is inside the UIScrollView</span>
    stackView.WidthAnchor
        .ConstraintEqualTo(scroll.WidthAnchor)
        .Active = <span class="hljs-literal">true</span>;
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> Random Random 
    = <span class="hljs-keyword">new</span> Random(DateTime.Now.Millisecond);

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> UIView <span class="hljs-title">GetRandomView</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> view = <span class="hljs-keyword">new</span> UIView();
    view.BackgroundColor = ColorUtil.GetRandomColor();
    view.Layer.CornerRadius = <span class="hljs-number">8</span>;

    <span class="hljs-keyword">var</span> randomHeight = Random.Next(<span class="hljs-number">25</span>, <span class="hljs-number">200</span>);
    view.HeightAnchor.ConstraintEqualTo(randomHeight).Active = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">return</span> view;
}
</code></pre>
<p>I´m using here two custom method extensions: <a target="_blank" href="https://github.com/xleon/UIStackViewPlayground/blob/master/Helpers/ConstraintUtil.cs#L12">FullSizeOf()</a> and <a target="_blank" href="https://github.com/xleon/UIStackViewPlayground/blob/master/Helpers/ConstraintUtil.cs#L87">EnableAutoLayout</a> to make the code cleaner.</p>
<h2 id="heading-nested-stacks">Nested stacks</h2>
<p>Nesting stacks is no mystery as it works like nesting any other view.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366384620/d69ab020-1805-4916-8fb4-132938b3cf4c.gif" alt class="image--center mx-auto" /></p>
<p>The layout hierarchy looks like the following:</p>
<pre><code class="lang-csharp"><span class="hljs-function">UIScrollView
    UIStackView
        <span class="hljs-title">UIView</span> (<span class="hljs-params">with gray background</span>)
            UILabel
            UIStackView
                Children
        <span class="hljs-title">UIView</span> (<span class="hljs-params">with gray background</span>)
            UILabel
            UIStackView
                Children
        ...</span>
</code></pre>
<p>Grab the source code for this screen <a target="_blank" href="https://github.com/xleon/UIStackViewPlayground/blob/master/Views/NestedStacksViewController.cs">here</a></p>
<h2 id="heading-addremove-elements-with-animation">Add/remove elements with animation</h2>
<p>Animating constraints in iOS has been always simple:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// add/remove/change constraints and then...</span>
UIView.Animate(<span class="hljs-number">0.3</span>, () =&gt; view.LayoutIfNeeded());
</code></pre>
<p>Working with <code>UIViewStack</code> is not different. When adding or removing a view from the stack it will internally change the constraints with AutoLayout so the animation works in the same way. Let´s create a button that will add an arranged subview with a random color to the stack. Then destroy that view when the user taps on it:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ViewDidLoad</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">base</span>.ViewDidLoad();

    <span class="hljs-keyword">var</span> stack = <span class="hljs-keyword">new</span> UIStackView
    {
        TranslatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>,
        Axis = UILayoutConstraintAxis.Vertical,
        Alignment = UIStackViewAlignment.Fill,
        Distribution = UIStackViewDistribution.FillEqually,
        Spacing = <span class="hljs-number">4</span>
    };

    Add(stack);

    stack.FullSizeOf(View);

    <span class="hljs-keyword">var</span> addButton = <span class="hljs-keyword">new</span> UIButton(UIButtonType.System);
    addButton.SetTitle(<span class="hljs-string">"Add view"</span>, UIControlState.Normal);
    addButton.TouchUpInside += (s, e) =&gt;
    {
        stack.InsertArrangedSubview(GetAutoDestroyButton(), <span class="hljs-number">1</span>);
        UIView.Animate(<span class="hljs-number">0.3</span>, () =&gt; stack.LayoutIfNeeded());
    };

    stack.AddArrangedSubview(addButton);
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> UIButton <span class="hljs-title">GetAutoDestroyButton</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> button = <span class="hljs-keyword">new</span> UIButton
    {
        BackgroundColor = ColorUtil.GetRandomColor(<span class="hljs-literal">true</span>),
        ClipsToBounds = <span class="hljs-literal">true</span>
    };

    button.SetTitle(<span class="hljs-string">"Destroy"</span>, UIControlState.Normal);
    button.TouchUpInside += (sender, args) =&gt; 
        UIView.Animate(<span class="hljs-number">0.3</span>, () =&gt; 
            button.Hidden = <span class="hljs-literal">true</span>, button.RemoveFromSuperview);

    <span class="hljs-keyword">return</span> button;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366517800/b7ded2ca-eaab-433e-b141-b1c3e85f8a34.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-accordion-component-poc">Accordion component POC</h2>
<p>In a real application, I would probably use a <code>UITableView</code> to create an accordion component because it will handle items and scroll more efficiently using cell virtualization. However, this can be done faster with a <code>UIStackView</code> and it works pretty decently for a few views:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366639072/e41cdf56-6edc-4c1c-820b-bc5abfc6806c.gif" alt class="image--center mx-auto" /></p>
<p>In this sample, only a single item can be opened at a time. The content view is a resized <code>UILabel</code> with a random height, but it could be anything else.</p>
<p>It´s worth saying that content views in this sample are all added on initialization and later hidden and shown. This means that <code>UIStackView</code> is <strong>smart enough</strong> to handle constraint changes not only when views are added or removed, but also <strong>when their visibility changes</strong> (<code>view.Hidden = true|false</code>), simplifying your code even more.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> Random Random 
    = <span class="hljs-keyword">new</span> Random(DateTime.Now.Millisecond);

<span class="hljs-keyword">private</span> UIStackView _stackView;
<span class="hljs-keyword">private</span> UIView _visibleContent;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ViewDidLoad</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">base</span>.ViewDidLoad();

    _stackView = <span class="hljs-keyword">new</span> UIStackView
    {
        TranslatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>,
        Axis = UILayoutConstraintAxis.Vertical,
        Alignment = UIStackViewAlignment.Fill,
        Distribution = UIStackViewDistribution.EqualSpacing,
        Spacing = <span class="hljs-number">1</span>
    };

    <span class="hljs-keyword">var</span> scroll = <span class="hljs-keyword">new</span> UIScrollView();
    Add(scroll);

    scroll.AddSubview(_stackView);
    scroll.FullSizeOf(View);
    scroll.EnableAutoLayout();

    _stackView.FullSizeOf(scroll);
    _stackView.WidthAnchor
        .ConstraintEqualTo(scroll.WidthAnchor)
        .Active = <span class="hljs-literal">true</span>;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">25</span>; i++)
    {
        _stackView.AddArrangedSubview(
            GetButton(<span class="hljs-string">$"Category <span class="hljs-subst">{i + <span class="hljs-number">1</span>}</span>"</span>, i));

        _stackView.AddArrangedSubview(
            GetContent(<span class="hljs-string">$"Child of category <span class="hljs-subst">{i + <span class="hljs-number">1</span>}</span>"</span>, i + <span class="hljs-number">100</span>));
    }
}

<span class="hljs-function"><span class="hljs-keyword">private</span> UIButton <span class="hljs-title">GetButton</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> title, <span class="hljs-keyword">int</span> tag</span>)</span>
{
    <span class="hljs-keyword">var</span> button = <span class="hljs-keyword">new</span> UIButton
    {
        BackgroundColor = UIColor.Blue,
        HorizontalAlignment = UIControlContentHorizontalAlignment.Left,
        ContentEdgeInsets = <span class="hljs-keyword">new</span> UIEdgeInsets(<span class="hljs-number">4</span>, <span class="hljs-number">10</span>, <span class="hljs-number">4</span>, <span class="hljs-number">10</span>),
        ClipsToBounds = <span class="hljs-literal">true</span>,
        Tag = tag
    };

    button.SetTitle(title, UIControlState.Normal);

    button.TouchUpInside += (sender, args) =&gt;
    {
        <span class="hljs-keyword">var</span> content = View.ViewWithTag(((UIButton) sender).Tag + <span class="hljs-number">100</span>);
        UIView.Animate(<span class="hljs-number">0.3</span>, () =&gt;
        {
            <span class="hljs-keyword">if</span> (_visibleContent != <span class="hljs-literal">null</span>)
                _visibleContent.Hidden = <span class="hljs-literal">true</span>;

            <span class="hljs-keyword">if</span>(!Equals(_visibleContent, content))
                content.Hidden = !content.Hidden;

        }, () =&gt; _visibleContent = content.Hidden ? <span class="hljs-literal">null</span> : content);
    };

    <span class="hljs-keyword">return</span> button;
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> UILabel <span class="hljs-title">GetContent</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> title, <span class="hljs-keyword">int</span> tag</span>)</span>
{
    <span class="hljs-keyword">var</span> content = <span class="hljs-keyword">new</span> UILabel
    {
        Text = title,
        Lines = <span class="hljs-number">0</span>,
        TextAlignment = UITextAlignment.Center,
        TextColor = UIColor.Black,
        BackgroundColor = UIColor.LightGray,
        Tag = tag,
        Hidden = <span class="hljs-literal">true</span>
    };

    content.HeightAnchor
        .ConstraintEqualTo(Random.Next(<span class="hljs-number">40</span>, <span class="hljs-number">250</span>))
        .Active = <span class="hljs-literal">true</span>;

    <span class="hljs-keyword">return</span> content;
}
</code></pre>
<h2 id="heading-change-axis-with-animation">Change axis with animation</h2>
<p>I don´t see any useful case to change the axis in a real app, but here it is. Changing from horizontal to vertical and viceversa takes one line:</p>
<pre><code class="lang-csharp">stackView.Axis = UILayoutConstraintAxis.Horizontal;
stackView.Axis = UILayoutConstraintAxis.Vertical;
</code></pre>
<p>And if you want to animate it:</p>
<pre><code class="lang-csharp">UIView.Animate(<span class="hljs-number">0.3</span>, () =&gt;
{
    stackView.Axis = stackView.Axis == UILayoutConstraintAxis.Vertical
        ? UILayoutConstraintAxis.Horizontal
        : UILayoutConstraintAxis.Vertical;

    stackView.LayoutIfNeeded();
});
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366724838/54951e1b-8685-4708-b205-14991c4bf96c.gif" alt class="image--center mx-auto" /></p>
<p>Enough for now, you can find these and other samples in the <a target="_blank" href="https://github.com/xleon/UIStackViewPlayground">GitHub repo</a>.<br />Here it´s a screen recording with the rest of them:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698366744061/a7737650-01c6-404a-8f6a-ccc5d83fcadc.gif" alt class="image--center mx-auto" /></p>
<p>Remember that <code>UIStackView</code> is available from iOS 9. If you need to support previous versions, go ahead and try <a target="_blank" href="https://github.com/Cheesebaron/TZStackView">Cheesebaron TZStackView</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Getting fancy with UIView anchors and state changes]]></title><description><![CDATA[If you prefer coded user interfaces rather than designers (Xcode Interface Builder or Xamarin designers) you will surely like this API (available from iOS9+). It´s very readable, easy to maintain and feels more natural compared to the old one.
Every ...]]></description><link>https://xleon.net/getting-fancy-with-uiview-anchors-and-state-changes</link><guid isPermaLink="true">https://xleon.net/getting-fancy-with-uiview-anchors-and-state-changes</guid><category><![CDATA[Xamarin]]></category><category><![CDATA[iOS]]></category><category><![CDATA[anchors]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Thu, 06 Jul 2017 23:20:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698359212754/d2fc00a3-d2e6-4e5c-b566-e28aa17fc4fe.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you prefer coded user interfaces rather than designers (Xcode Interface Builder or Xamarin designers) you will surely like this API (available from iOS9+). It´s very readable, easy to maintain and feels more natural compared to the old one.</p>
<p>Every <code>UIView</code> has now several <code>NSLayoutAnchor</code> properties</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367241900/6cfb7f6a-8c96-4be5-97c2-809d2c88a410.png" alt class="image--center mx-auto" /></p>
<p>that can be used to create constraints (<code>NSLayoutCostraint</code>):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367254184/6f0a335e-fbac-4693-90e2-5f1c1a3b00c8.png" alt class="image--center mx-auto" /></p>
<p>Creating a constraint does not mean it will be active by default and you´ve got do it explicitly:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> c = origin.CenterXAnchor.ConstraintEqualTo(target.CenterXAnchor);
c.Active = <span class="hljs-literal">true</span>;

<span class="hljs-comment">// or just</span>
origin.CenterXAnchor
    .ConstraintEqualTo(target.CenterXAnchor)
    .Active = <span class="hljs-literal">true</span>;

<span class="hljs-comment">// activate multiple constraints at once:</span>
NSLayoutConstraint.ActivateConstraints(<span class="hljs-keyword">new</span>[]{
    origin.CenterXAnchor.ConstraintEqualTo(target.CenterXAnchor),
    origin.CenterYAnchor.ConstraintEqualTo(target.CenterYAnchor)
});
</code></pre>
<p>It´s really easy to create <code>UIView</code> method extensions. For instance, to center a view in its parent (following the above sample):</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> NSLayoutConstraint[] <span class="hljs-title">CenterIn</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> UIView origin, 
    UIView target, <span class="hljs-keyword">bool</span> activate = <span class="hljs-literal">true</span></span>)</span>
{
    origin.TranslatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">var</span> contraints = <span class="hljs-keyword">new</span>[]
    {
        origin.CenterXAnchor.ConstraintEqualTo(target.CenterXAnchor),
        origin.CenterYAnchor.ConstraintEqualTo(target.CenterYAnchor)
    };

    <span class="hljs-keyword">if</span> (activate)
        NSLayoutConstraint.ActivateConstraints(contraints);

    <span class="hljs-keyword">return</span> contraints;
}

<span class="hljs-comment">// use case</span>
view.CenterIn(parentView);
</code></pre>
<p>Now let´s adjust a view to its parent bounds:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> NSLayoutConstraint[] <span class="hljs-title">FullSizeOf</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> UIView origin, 
    UIView target, UIEdgeInsets edges, <span class="hljs-keyword">bool</span> activate = <span class="hljs-literal">true</span></span>)</span>
{
    origin.TranslatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">var</span> contraints = <span class="hljs-keyword">new</span>[]
    {
        origin.LeadingAnchor.ConstraintEqualTo(target.LeadingAnchor, edges.Left),
        origin.TrailingAnchor.ConstraintEqualTo(target.TrailingAnchor, -edges.Right),
        origin.TopAnchor.ConstraintEqualTo(target.TopAnchor, edges.Top),
        origin.BottomAnchor.ConstraintEqualTo(target.BottomAnchor, -edges.Bottom)
    };

    <span class="hljs-keyword">if</span>(activate)
        NSLayoutConstraint.ActivateConstraints(contraints);

    <span class="hljs-keyword">return</span> contraints;
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> NSLayoutConstraint[] <span class="hljs-title">FullSizeOf</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> UIView origin, 
    UIView target, <span class="hljs-keyword">float</span> margin = <span class="hljs-number">0</span>, <span class="hljs-keyword">bool</span> activate = <span class="hljs-literal">true</span></span>)</span> 
    =&gt; origin.FullSizeOf(target, <span class="hljs-keyword">new</span> UIEdgeInsets(margin, margin, margin, margin), activate);

<span class="hljs-comment">// use cases</span>
view.FullSizeOf(parentView);
view.FullSizeOf(parentView, <span class="hljs-number">10</span>); <span class="hljs-comment">// 10 points of margin</span>
view.FullSizeOf(parentView, <span class="hljs-keyword">new</span> UIEdgeInsets(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>, <span class="hljs-number">5</span>, <span class="hljs-number">10</span>));
</code></pre>
<p>And now let´s make a shortcut to set the width and height:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> NSLayoutConstraint[] <span class="hljs-title">ConstraintSize</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> UIView origin, 
    <span class="hljs-keyword">float</span> width, <span class="hljs-keyword">float</span> height, <span class="hljs-keyword">bool</span> activate = <span class="hljs-literal">true</span></span>)</span>
{
    origin.TranslatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">var</span> contraints = <span class="hljs-keyword">new</span>[]
    {
        origin.WidthAnchor.ConstraintEqualTo(width),
        origin.HeightAnchor.ConstraintEqualTo(height)
    };

    <span class="hljs-keyword">if</span> (activate)
        NSLayoutConstraint.ActivateConstraints(contraints);

    <span class="hljs-keyword">return</span> contraints;
}

<span class="hljs-comment">// use case</span>
view.ConstraintSize(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>);
</code></pre>
<p>Consider playing around with all anchors and <code>Constraint...To()</code> methods. You can make anything.</p>
<p>Those method extensions above return an array of constraints so we can deactivate/activate them later. Consider a set of constraints like a visual state we can store in a variable:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> small = view.ConstraintSize(<span class="hljs-number">100</span>, <span class="hljs-number">80</span>);
<span class="hljs-keyword">var</span> big = view.ConstraintSize(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-literal">false</span>);
</code></pre>
<p>States can get even more complex by chaining the constraints from multiple objects:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> small = view1.ConstraintSize(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>)
    .Concat(view2.ConstraintSize(<span class="hljs-number">50</span>, <span class="hljs-number">50</span>))
    .Concat(view3.ConstraintSize(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>))
    .ToArray();

<span class="hljs-keyword">var</span> big = view1.ConstraintSize(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-literal">false</span>)
    .Concat(view2.ConstraintSize(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-literal">false</span>))
    .Concat(view3.ConstraintSize(<span class="hljs-number">60</span>, <span class="hljs-number">60</span>, <span class="hljs-literal">false</span>))
    .ToArray();
</code></pre>
<h2 id="heading-state-changes">State changes</h2>
<p>Simply deactivate the old constraints and activate the new ones:</p>
<pre><code class="lang-csharp">NSLayoutConstraint.DeactivateConstraints(small);
NSLayoutConstraint.ActivateConstraints(big);
</code></pre>
<p>With the basic blocks in place, let´s create a new extension to get fancy with state changes:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ChangeState</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> UIView parentView, 
    NSLayoutConstraint[] before, 
    NSLayoutConstraint[] after, 
    <span class="hljs-keyword">double</span> duration = <span class="hljs-number">0</span></span>)</span>
{
    parentView.LayoutIfNeeded();

    NSLayoutConstraint.DeactivateConstraints(before);
    NSLayoutConstraint.ActivateConstraints(after);

    <span class="hljs-keyword">if</span> (duration &gt; <span class="hljs-number">0</span>)
    {
        UIView.Animate(duration, parentView.LayoutIfNeeded);
    }
}

<span class="hljs-comment">// use cases</span>
View.ChangeState(small, big);
View.ChangeState(big, small, duration:<span class="hljs-number">0.3</span>);
</code></pre>
<p>Here´s a working <a target="_blank" href="https://github.com/xleon/UIStackViewPlayground/blob/master/Views/AnchorPocViewController.cs">example</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367387855/6c4bae0e-0945-4665-beac-5c980467341b.gif" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[A simple page-indicator for your android view-pager]]></title><description><![CDATA[Every app I did need some kind of slider, like on-board or walkthroughs, product sliders, help tutorials, photo slide-shows, etc.
In the past, I´ve done some view pagers with those circle page indicators in Android that involved using either 3rd part...]]></description><link>https://xleon.net/a-simple-page-indicator-for-your-android-view-pager</link><guid isPermaLink="true">https://xleon.net/a-simple-page-indicator-for-your-android-view-pager</guid><category><![CDATA[Android]]></category><category><![CDATA[page-indicator]]></category><category><![CDATA[view-pager]]></category><category><![CDATA[Xamarin]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Sun, 28 May 2017 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698362938066/f8338a09-9ebe-4fda-b014-41283d0555dd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every app I did need some kind of slider, like on-board or walkthroughs, product sliders, help tutorials, photo slide-shows, etc.</p>
<p>In the past, I´ve done some view pagers with those circle page indicators in Android that involved using either 3rd party libraries or a ton of code. That was before Lollipop came out. This is now very obvious and ridiculously simple but I´m sharing it here in case you don´t know about it.</p>
<p>With a little xml we are going to convert this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698361268862/65b17250-d431-48d8-9d26-70acf93565b1.png" alt class="image--center mx-auto" /></p>
<p>into something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698361283983/3aadf77b-ef36-43be-9355-1f03b1272612.png" alt class="image--center mx-auto" /></p>
<p>Making the connection between the view-pager and our page indicator (<code>TabLayout</code>) takes a single line:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> viewPager = FindViewById&lt;ViewPager&gt;(Resource.Id.viewpager);
viewPager.Adapter = <span class="hljs-keyword">new</span> MyPagerAdapter(SupportFragmentManager, funkyList);

<span class="hljs-keyword">var</span> dots = FindViewById&lt;TabLayout&gt;(Resource.Id.dots);
dots.SetupWithViewPager(viewPager, <span class="hljs-literal">true</span>); <span class="hljs-comment">// &lt;- magic here</span>
</code></pre>
<p>So we now need to make a <code>TabLayout</code> look like a page indicator.<br />The good news are that it´s pretty easy:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">LinearLayout</span>
    <span class="hljs-attr">android:orientation</span>=<span class="hljs-string">"vertical"</span>
    <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
    <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"wrap_content"</span>
    <span class="hljs-attr">android:paddingLeft</span>=<span class="hljs-string">"@dimen/activity_horizontal_margin"</span>
    <span class="hljs-attr">android:paddingRight</span>=<span class="hljs-string">"@dimen/activity_horizontal_margin"</span>
    <span class="hljs-attr">android:paddingBottom</span>=<span class="hljs-string">"@dimen/activity_horizontal_margin"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">android.support.v4.view.ViewPager</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/viewpager"</span>
        <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
        <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"250dp"</span>
        <span class="hljs-attr">android:layout_marginBottom</span>=<span class="hljs-string">"5dp"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">android.support.design.widget.TabLayout</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/dots"</span>
        <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
        <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"26dp"</span>
        <span class="hljs-attr">local:tabBackground</span>=<span class="hljs-string">"@drawable/dot_selector"</span>
        <span class="hljs-attr">local:tabGravity</span>=<span class="hljs-string">"center"</span>
        <span class="hljs-attr">local:tabIndicatorHeight</span>=<span class="hljs-string">"0dp"</span>
        <span class="hljs-attr">local:tabPaddingStart</span>=<span class="hljs-string">"7dp"</span>
        <span class="hljs-attr">local:tabPaddingEnd</span>=<span class="hljs-string">"7dp"</span>/&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">LinearLayout</span>&gt;</span>
</code></pre>
<p><code>tabPaddingStart</code> and <code>tabPaddingEnd</code> will define the separation between the dots.</p>
<p>I´ve used a <code>LinearLayout</code> here for brevity but you could use whatever layout you need. It doesn´t matter.</p>
<p>To have <code>TabLayout</code> available in your project you must install the Nuget <code>Xamarin.Android.Support.Design</code>.</p>
<p>Now take a look at the <code>dot_selector.xml</code>. It defines the selected/unselected states:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">selector</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">item</span> <span class="hljs-attr">android:drawable</span>=<span class="hljs-string">"@drawable/selected_dot"</span> <span class="hljs-attr">android:state_selected</span>=<span class="hljs-string">"true"</span>/&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">item</span> <span class="hljs-attr">android:drawable</span>=<span class="hljs-string">"@drawable/default_dot"</span>/&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">selector</span>&gt;</span>
</code></pre>
<p>The selected state (<code>selected_dot.xml</code>):</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">layer-list</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">item</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">shape</span>
      <span class="hljs-attr">android:innerRadius</span>=<span class="hljs-string">"0dp"</span>
      <span class="hljs-attr">android:shape</span>=<span class="hljs-string">"ring"</span>
      <span class="hljs-attr">android:thickness</span>=<span class="hljs-string">"4dp"</span>
      <span class="hljs-attr">android:useLevel</span>=<span class="hljs-string">"false"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">solid</span> <span class="hljs-attr">android:color</span>=<span class="hljs-string">"@color/page_indicator_selected_color"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">shape</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">item</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">layer-list</span>&gt;</span>
</code></pre>
<p>And the default state (<code>default_dot.xml</code>), that is pretty much the same with a different color:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">layer-list</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">item</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">shape</span>
      <span class="hljs-attr">android:innerRadius</span>=<span class="hljs-string">"0dp"</span>
      <span class="hljs-attr">android:shape</span>=<span class="hljs-string">"ring"</span>
      <span class="hljs-attr">android:thickness</span>=<span class="hljs-string">"4dp"</span>
      <span class="hljs-attr">android:useLevel</span>=<span class="hljs-string">"false"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">solid</span> <span class="hljs-attr">android:color</span>=<span class="hljs-string">"@color/page_indicator_default_color"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">shape</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">item</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">layer-list</span>&gt;</span>
</code></pre>
<p>As you may guess, <code>android:thickness</code> is the size of the dot.</p>
<p>That´s it!</p>
]]></content:encoded></item><item><title><![CDATA[Large file downloads on Windows 10 mobile]]></title><description><![CDATA[To download big files correctly (especially on mobile devices) we should try to achieve the following goals:

Separate the download process from the current view in a background task: we don´t want the download to stop if we navigate to another view ...]]></description><link>https://xleon.net/large-file-downloads-on-windows-10-mobile</link><guid isPermaLink="true">https://xleon.net/large-file-downloads-on-windows-10-mobile</guid><category><![CDATA[UWP]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Fri, 07 Apr 2017 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/CcvYVwEzZSs/upload/2515461dcc60f4249b72f0c54723980a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To download big files correctly (especially on mobile devices) we should try to achieve the following goals:</p>
<ul>
<li><p>Separate the download process from the current view in a background task: we don´t want the download to stop if we navigate to another view or the device goes idle</p>
</li>
<li><p>The download stream <strong>should not write directly to memory</strong>: the device may run out of memory very soon, crashing your app. Instead, we should write to the file system directly as soon as the bytes are downloaded.</p>
</li>
<li><p>Report progress: users should know how long it will take to finish. Otherwise, it may look like it´s stuck</p>
</li>
</ul>
<p>The same rules apply to any operating system, like iOS and Android (that will come in a future post).</p>
<p>It turns out that doing all this on Windows RT is fairly easy thanks to the class <code>Windows.Networking.BackgroundTransfer.BackgroundDownloader</code>, because it will follow all the rules:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> downloader = <span class="hljs-keyword">new</span> BackgroundDownloader();
<span class="hljs-keyword">var</span> download = downloader.CreateDownload(uri, destinationFile);
download.Priority = BackgroundTransferPriority.High;

<span class="hljs-keyword">var</span> progressCallback = <span class="hljs-keyword">new</span> Progress&lt;DownloadOperation&gt;(operation =&gt;
{
    <span class="hljs-keyword">if</span> (download.Progress.TotalBytesToReceive &gt; <span class="hljs-number">0</span>)
    {
        <span class="hljs-keyword">var</span> percent = download.Progress.BytesReceived * <span class="hljs-number">100</span> / download.Progress.TotalBytesToReceive;
        <span class="hljs-comment">// TODO report percent</span>
    }
});

<span class="hljs-keyword">await</span> download.StartAsync().AsTask(progressCallback);
<span class="hljs-comment">// at this point the download has been completed</span>
</code></pre>
<p>That´s it actually, but to implement this in a cross-platform way (thinking about iOS and Android and Xamarin.Forms) I created an event <code>OnDownloadPercentProgress</code> and wrapped it all with a platform-agnostic interface:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> PCLStorage;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Core.Helpers</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IFileManager</span>
    {
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Parameter double is the download percent</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Parameter string is the uri string currently being downloaded</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-keyword">event</span> Action&lt;<span class="hljs-keyword">double</span>, <span class="hljs-keyword">string</span>&gt; OnDownloadPercentProgress;


        <span class="hljs-function">Task&lt;IFile&gt; <span class="hljs-title">Download</span>(<span class="hljs-params">Uri uri, IFolder downloadFolder, <span class="hljs-keyword">string</span> desiredFileName = <span class="hljs-literal">null</span></span>)</span>;
    }
}
</code></pre>
<p><code>IFolder</code> comes from the cross-platform storage library called <a target="_blank" href="https://github.com/dsplaisted/PCLStorage">PCLStorage</a></p>
<p>And here is the implementation on Windows:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.IO;
<span class="hljs-keyword">using</span> System.IO.Compression;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Windows.Networking.BackgroundTransfer;
<span class="hljs-keyword">using</span> Windows.Storage;
<span class="hljs-keyword">using</span> Core.Helpers;
<span class="hljs-keyword">using</span> PCLStorage;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">UWP.Helpers</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FileManager</span> : <span class="hljs-title">IFileManager</span>
    {
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">event</span> Action&lt;<span class="hljs-keyword">double</span>, <span class="hljs-keyword">string</span>&gt; OnDownloadPercentProgress;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IFile&gt; <span class="hljs-title">Download</span>(<span class="hljs-params">Uri uri, IFolder downloadFolder, <span class="hljs-keyword">string</span> desiredFileName = <span class="hljs-literal">null</span></span>)</span>
        {
            desiredFileName = desiredFileName ?? Path.GetFileName(uri.LocalPath);

            <span class="hljs-keyword">var</span> localFolder = <span class="hljs-keyword">await</span> StorageFolder.GetFolderFromPathAsync(downloadFolder.Path);
            <span class="hljs-keyword">var</span> destinationFile = <span class="hljs-keyword">await</span> localFolder.CreateFileAsync(desiredFileName, Windows.Storage.CreationCollisionOption.GenerateUniqueName);

            <span class="hljs-keyword">var</span> downloader = <span class="hljs-keyword">new</span> BackgroundDownloader();
            <span class="hljs-keyword">var</span> download = downloader.CreateDownload(uri, destinationFile);
            download.Priority = BackgroundTransferPriority.High;

            <span class="hljs-keyword">var</span> progressCallback = <span class="hljs-keyword">new</span> Progress&lt;DownloadOperation&gt;(operation =&gt;
            {
                <span class="hljs-keyword">if</span> (download.Progress.TotalBytesToReceive &gt; <span class="hljs-number">0</span>)
                {
                    <span class="hljs-keyword">var</span> percent = download.Progress.BytesReceived * <span class="hljs-number">100</span> / download.Progress.TotalBytesToReceive;
                    OnDownloadPercentProgress?.Invoke(percent, uri.ToString());
                }
            });

            <span class="hljs-keyword">await</span> download.StartAsync().AsTask(progressCallback);

            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> downloadFolder.GetFileAsync(desiredFileName);
        }
    }
}
</code></pre>
<p>We are done.</p>
]]></content:encoded></item><item><title><![CDATA[UWP mobile side loading]]></title><description><![CDATA[Installing universal Windows apps outside of the store is a bit different than iOS or Android. HockeyApp documentation explains how to do it but they are missing some important (and perhaps frustrating) details that I will explain below.
Actually, yo...]]></description><link>https://xleon.net/uwp-mobile-side-loading</link><guid isPermaLink="true">https://xleon.net/uwp-mobile-side-loading</guid><category><![CDATA[UWP]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Thu, 06 Apr 2017 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/0VGG7cqTwCo/upload/1fc22602b6a8de9925a7585223b9ad55.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Installing universal Windows apps outside of the store is a bit different than iOS or Android. <a target="_blank" href="https://support.hockeyapp.net/kb/client-integration-windows-and-windows-phone/how-to-sideload-uwp-applications">HockeyApp documentation</a> explains how to do it but they are missing some important (and perhaps frustrating) details that I will explain below.</p>
<p>Actually, you don´t need HockeyApp to side-load Windows 10 apps as you can upload your build wherever you want, but obviously, with HockeyApp you get many more features for free, like crash reporting, distribution management, event logging, integration with Visual Studio, etc. <a target="_blank" href="https://mobile.azure.com">Visual Studio Mobile Center</a> is the next generation of HockeyApp but as of today it doesn´t support Windows Universal apps.</p>
<h2 id="heading-compile-a-release-build-for-mobile">Compile a release build for mobile</h2>
<p>Not much to add to the HockeyApp doc in this step. Check it out <a target="_blank" href="https://support.hockeyapp.net/kb/client-integration-windows-and-windows-phone/how-to-sideload-uwp-applications#build-application">here</a>, but bear in mind that in the last step (architecture check-boxes), you should only check ARM if your app is not targeting desktop devices. The bundle size will decrease a lot.</p>
<h2 id="heading-upload-the-build-to-hockeyapp">Upload the build to HockeyApp</h2>
<p>The result of the previous step is a new folder (i.e: <em>YourApp_1.0.0.0_Test</em>) in the <em>AppPackages</em> directory of your UWP project with the following contents:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367563486/2dd37d2e-e4c9-4292-aefe-d0ae37588c8a.png" alt class="image--center mx-auto" /></p>
<p>You´ve got two options here:</p>
<ol>
<li><p>Upload the single .appxbundle file</p>
</li>
<li><p>Zip the whole directory and upload it to HockeyApp (this is the same thing Visual Studio does for you when right-clicking on the project &gt; HockeyApp &gt; Distribute app…)</p>
</li>
</ol>
<p>Whether you choose 1 or 2 will affect how users install the app:</p>
<p>If you choose 1), installing the app is easier, as the user will just click the “Download” button on the HockeyApp web page, and then click the downloaded file to install it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367592892/bb2ca8b7-72cf-4469-b038-9fae46c28a33.png" alt class="image--center mx-auto" /></p>
<p>Going for option 2 means that a user will download a zip to the mobile device, open that zip and then tap on the .appxbundle file to finally install the app. Windows 10 Mobile cannot open zips out of the box, so a third-party app like <a target="_blank" href="https://www.microsoft.com/es-es/store/p/zip-archiver/9wzdncrd1l2f">Zip Archiver</a> should be installed first.</p>
<p>Why on earth would you want to make things harder with a zip file and why is it the default option in Visual Studio? See below on “Installing dependencies”</p>
<h2 id="heading-upload-symbols-to-hockeyapp">Upload symbols to HockeyApp</h2>
<p>To see better crash information and stack traces, right after the previous step, drag the <em>.appxsym</em> file to the HockeyApp page. Then, go to <em>ProjectFolder/bin/ARM</em>, zip the file <em>YourAppName.pdb</em> and drag <em>YourAppName.zip</em> to the same page. There is also a button to “Upload build or sym” and another to “Upload zipped .pdb”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367635991/1337ffef-ad0b-480b-a308-5f553bec652b.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-device-setup">Device setup</h2>
<p>To install any app compiled with a test certificate or coming from an untrusted source (like HockeyApp), the user needs to enable Windows developer mode on the device. Go to settings &gt; Update &amp; Security &gt; For developers and select “Developer mode”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367646912/0c1a4b32-6edf-4c96-acb8-d62c3fea845f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-installing-dependencies">Installing dependencies</h2>
<p>When you debug your app through a USB connected device, Visual Studio will install all dependencies for you in the background and everything will just work. However, other users trying to install your app will open the .appxbundle, install the app and it won´t appear anywhere on the device. Windows won´t let them know something wrong happened (the installation failed silently without errors).</p>
<p>This happens when dependencies are not installed on the user´s device because they don´t get installed automatically (BTW, a simple “please, install dependencies” alert would have saved me many wasted hours thinking that I did something wrong with the app bundle package).</p>
<p>Dependencies need to be installed usually once per device unless you release a new version of your app that targets a newer version of Windows. In that case, users will need to install them again. It´s a good idea to inform the users and testers about these details before they try to install them for the first time.</p>
<p>Installing dependencies involves having them on the device and then clicking/tapping on each one to confirm the installation. They are located in the “Dependencies” folder inside the zip you created previously:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698367688431/eccb554a-4f7d-4280-91b6-218ee5346ff6.png" alt class="image--center mx-auto" /></p>
<p><em>Keep in mind that if your release build is composed of the single .appxbundle file, you are assuming the user already installed these dependencies.</em></p>
<h2 id="heading-installing-the-application">Installing the application</h2>
<p>Last, but not least, tap on the .appxbundle to install the app. Everything should work now. For the next version release, this is the only step needed.</p>
]]></content:encoded></item><item><title><![CDATA[SQLite.NET > async VS sync]]></title><description><![CDATA[Many developers are happy using TPL (Task Parallel Library) with SQLite.Net. It´s not hard to find blog posts, StackOverflow answers and other sites recommending it. The library offers an async connection that can be handy to avoid blocking the UI th...]]></description><link>https://xleon.net/sqlitenet-async-vs-sync</link><guid isPermaLink="true">https://xleon.net/sqlitenet-async-vs-sync</guid><category><![CDATA[Xamarin]]></category><category><![CDATA[SQLite]]></category><category><![CDATA[async]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Wed, 15 Feb 2017 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ZfVyuV8l7WU/upload/f57180b7e081768f41d4c17ba42064c1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Many developers are happy using <a target="_blank" href="https://www.codeproject.com/Articles/152765/Task-Parallel-Library-of-n">TPL</a> (Task Parallel Library) with <a target="_blank" href="https://github.com/praeclarum/sqlite-net">SQLite.Net</a>. It´s not hard to find blog posts, StackOverflow answers and other sites recommending it. The library offers an async connection that can be handy to avoid blocking the UI thread and that´s how <a target="_blank" href="https://stackoverflow.com/questions/29050400/generic-repository-for-sqlite-net-in-xamarin-project/29856945#29856945">I implemented SQLite.Net from day one</a>.</p>
<p>The reason to use <a target="_blank" href="https://github.com/praeclarum/sqlite-net/blob/master/src/SQLiteAsync.cs#L45">SQLiteAsyncConnection</a> is pretty simple: I want my app to stay responsive when querying the database. Big queries take time. If the connection is synchronous my app may become unresponsive for a while until the data is retrieved. We care about users and C# async/await is relatively easy, so let´s use it everywhere!, right?</p>
<h2 id="heading-well-sqlite-is-not-async">Well… sqlite is NOT async.</h2>
<p>That´s right. Sqlite accesses data synchronously, no matter what. Async connections are fake 💩. Simple as that. Taking a look at the <a target="_blank" href="https://github.com/praeclarum/sqlite-net/blob/master/src/SQLiteAsync.cs#L169">source code</a>, you´ll find out that every method call is wrapped within a Task:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Task</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">GetAsync</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">object</span> pk</span>) <span class="hljs-keyword">where</span> T : <span class="hljs-title">new</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">return</span> Task.Factory.StartNew(() =&gt;
    {
        <span class="hljs-keyword">var</span> conn = GetConnection();
        <span class="hljs-keyword">using</span> (conn.Lock())
        {
            <span class="hljs-keyword">return</span> conn.Get&lt;T&gt;(pk);
        }
    });
}
</code></pre>
<p>Okay… so <em>what´s wrong with it?</em></p>
<h2 id="heading-you-are-forced-to-use-async-wrappers-everywhere">You are forced to use async wrappers everywhere</h2>
<p>You can´t choose to make something async only when it´s needed. Once you set up an async connection, you will be forced to use these wrappers everywhere unless you create a new sync connection. What if you find out that a query is fast enough to be sync?</p>
<ol>
<li><p>Would you stick to async/await anyway?</p>
</li>
<li><p>Would you create a new sync connection for those cases?</p>
</li>
</ol>
<p>If you choose 1, <em>you are complicating things</em>. I mean: you are awaiting a <code>Task</code> rather than a direct and simple method call, forcing your method to be async. I found out that in mobile development (mostly in UI initialization logic), async is <a target="_blank" href="http://stackoverflow.com/search?q=xamarin+async+initialization">harder to manage</a>. Database queries are usually made on-screen initialization, so there you have it. You still can make it worst, by getting the result of a task in a sync fashion: <code>var user = connection.GetAsync&lt;User&gt;(1).Result</code> so you end up using a sync method, wrapped in a Task to be async, and then converted back to sync. Well played! :punch:</p>
<p>If you choose 2, <em>you are complicating things</em>. The best way to work with SQLite in most cases is by reusing a single connection during the whole life cycle.</p>
<h2 id="heading-you-need-to-be-careful-with-concurrency-and-locking-errors">You need to be careful with concurrency and locking errors</h2>
<p>Just take a look <a target="_blank" href="https://forums.xamarin.com/discussion/549/sqlite-net-and-multiple-threads">here</a> or <a target="_blank" href="https://www.google.es/search?q=sqlite-net-pcl+sqlite+busy&amp;oq=sqlite-net-pcl+sqlite+busy&amp;aqs=chrome..69i57.6194j0j9&amp;sourceid=chrome&amp;ie=UTF-8#q=xamarin+sqlite+busy">here</a> or <a target="_blank" href="https://bitbucket.org/twincoders/sqlite-net-extensions/issues/60/async-db-operations-sqliteexception-busy">here</a>. The same can happen with sync connections, but it´s easier to fix:</p>
<pre><code class="lang-csharp">Connection = <span class="hljs-keyword">new</span> SQLiteConnection(path, SQLiteOpenFlags.ReadWrite 
    | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex);
</code></pre>
<p>I´ve never had a single issue when creating the connection that way.</p>
<h2 id="heading-how-many-of-your-queries-really-take-so-long-to-be-async">How many of your queries really take so long to be async?</h2>
<p>I´m pretty sure that if you <a target="_blank" href="https://github.com/Fody/MethodTimer">measure the time</a> it takes for most of your queries to run, you´ll realize they are surprisingly fast. Fast enough so that the user won´t notice a delay, therefore async here is useless. Go ahead and try it.</p>
<h2 id="heading-what-happens-with-blocking-time-consuming-queries">What happens with blocking, time-consuming queries?</h2>
<p>In those cases, you can/should just make the async wrapper yourself. Besides, your code will provide more information: other developers working with your code will easily figure out which queries are fast enough (sync) and which are not (async).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<blockquote>
<p>KISS is an acronym for "Keep it simple, stupid". The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore simplicity should be a key goal in design and unnecessary complexity should be avoided.</p>
<p>Wikipedia</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Making iOS forms usable]]></title><description><![CDATA[Scenario:
Have you been through that moment when you click a form text field and the soft keyboard shows up overlaying the text field and you can´t see what you are writing?
Let´s make it worse:

No way to hide the keyboard (How can I tap the damn bu...]]></description><link>https://xleon.net/making-ios-forms-usable</link><guid isPermaLink="true">https://xleon.net/making-ios-forms-usable</guid><category><![CDATA[iOS]]></category><category><![CDATA[forms]]></category><category><![CDATA[keyboard]]></category><category><![CDATA[Xamarin]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Sat, 11 Feb 2017 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698364721527/f97b2386-9c92-40b2-ad51-c8cbb47b709e.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-scenario">Scenario:</h3>
<p>Have you been through that moment when you click a form text field and the soft keyboard shows up overlaying the text field and you can´t see what you are writing?</p>
<p>Let´s make it worse:</p>
<ul>
<li><p>No way to hide the keyboard (How can I tap the damn button below?).</p>
</li>
<li><p>The “next” or “intro” keys of the keyboard do exactly nothing.</p>
</li>
</ul>
<p>These problems and others may end up with your users leaving the app and never coming back.</p>
<h3 id="heading-did-you-let-that-happen">Did you let that happen?</h3>
<p>I´m pretty sure you won´t let that happen if you are a <a target="_blank" href="https://www.linkedin.com/in/decentdeveloper">decent developer</a> :flushed:, but I´ve seen this in more than a few apps (an <em>Apple reviewer may have been drunk at the time</em>) and I want to stop it now 🎷.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=U9t-slLl30E&amp;t=1s">https://www.youtube.com/watch?v=U9t-slLl30E&amp;t=1s</a></div>
<p> </p>
<h2 id="heading-lets-get-to-work">Let´s get to work</h2>
<p>Before we start writing code to control keyboard behavior we need to create a container view for our form that can scroll.</p>
<h3 id="heading-creating-scrollable-containers-with-interface-builder-and-autolayout">Creating scrollable containers with Interface Builder and AutoLayout</h3>
<p>You can´t just drop your form elements into a scroll view. That won´t work if you want to center the form vertically (i.e: a login screen). The form must be wrapped within its container.</p>
<ol>
<li><p>Place a <code>UIScrollView</code> fitting the full size of the view controller</p>
</li>
<li><p>Create a content <code>UIView</code> inside the scroll view, fitting the full size as well</p>
</li>
<li><p>Add your form elements to the content view</p>
</li>
<li><p>Add necessary constraints to make everything work with Autolayout</p>
</li>
</ol>
<p>This can be challenging if you´ve never done it before, so here is an awesome step-by-step video showing you exactly how to proceed with Xcode:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=UnQsFlMGDsI">https://www.youtube.com/watch?v=UnQsFlMGDsI</a></div>
<p> </p>
<p><em>My favourite visual designer is Xcode Interface Builder which is set as my</em> <a target="_blank" href="http://www.colbylwilliams.com/2016/09/26/default-designer.html"><em>default visual designer</em></a> <em>on Xamarin Studio. Visual Studio / Xamarin designers are the alternative but I could not get them to work properly yet.</em></p>
<h3 id="heading-creating-scrollable-containers-with-code">Creating scrollable containers with code</h3>
<p>If you don´t like designers, the same can be done with code.<br />I´m going to replicate the same layout of the video above with <a target="_blank" href="https://github.com/FluentLayout/Cirrious.FluentLayout">FluentLayout</a> as it simplifies constraints creation substantially.</p>
<p><strong><em>FormViewController.cs:</em></strong></p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Create containers</span>
<span class="hljs-keyword">var</span> contentView = <span class="hljs-keyword">new</span> UIView();
<span class="hljs-keyword">var</span> scrollView = <span class="hljs-keyword">new</span> UIScrollView {contentView};
Add(scrollView);

<span class="hljs-comment">// Create form elements</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">20</span>; i++)
{
    contentView.Add(<span class="hljs-keyword">new</span> UITextField
    {
        Placeholder = <span class="hljs-string">$"Test <span class="hljs-subst">{i + <span class="hljs-number">1</span>}</span>"</span>,
        BorderStyle = UITextBorderStyle.RoundedRect
    });
}

<span class="hljs-keyword">var</span> loginButton = <span class="hljs-keyword">new</span> UIButton(UIButtonType.System);
loginButton.SetTitle(<span class="hljs-string">"Login"</span>, UIControlState.Normal);

contentView.Add(loginButton);

<span class="hljs-comment">// Auto layout</span>
View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(scrollView.FullWidthOf(View));
View.AddConstraints(scrollView.FullHeightOf(View));
View.AddConstraints(
    contentView.WithSameWidth(View),
    contentView.WithSameHeight(View).SetPriority(UILayoutPriority.DefaultLow)
);

scrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
scrollView.AddConstraints(contentView.FullWidthOf(scrollView));
scrollView.AddConstraints(contentView.FullHeightOf(scrollView));

<span class="hljs-keyword">var</span> formConstraints = contentView
    .VerticalStackPanelConstraints(<span class="hljs-keyword">new</span> Margins(<span class="hljs-number">20</span>), contentView.Subviews);

<span class="hljs-comment">// very important to make scrolling work</span>
<span class="hljs-keyword">var</span> bottomViewConstraint = contentView.Subviews.Last()
    .AtBottomOf(contentView).Minus(<span class="hljs-number">20</span>);

contentView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
contentView.AddConstraints(formConstraints);
contentView.AddConstraints(bottomViewConstraint);
</code></pre>
<p>This is what we’ve got so far:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698359783271/09577e67-beb7-4527-9345-f493ce3aba29.gif" alt class="image--center mx-auto" /></p>
<p>Notice that once we tap on the bottom text view, the keyboard appears leaving the content behind. We can´t even scroll down to the bottom and there´s no way to hide the keyboard.</p>
<h3 id="heading-hiding-the-keyboard-when-tapping-on-the-view-background">Hiding the keyboard when tapping on the view background</h3>
<p>This doesn´t solve the problem, but at least the user will have a chance to visualize the whole content again. We will detect tap gestures on the controller´s <code>View</code> and just react when the trigger is not a <code>UIControl</code> (i.e.: text field or button). A method extension will allow us to reuse the behaviour on any screen:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> UITapGestureRecognizer <span class="hljs-title">DismissKeyboardOnTap</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> UIView view</span>)</span>
{
    <span class="hljs-comment">// Add gesture recognizer to hide keyboard</span>
    <span class="hljs-keyword">var</span> tap = <span class="hljs-keyword">new</span> UITapGestureRecognizer { CancelsTouchesInView = <span class="hljs-literal">false</span> };
    tap.AddTarget(() =&gt; view.EndEditing(<span class="hljs-literal">true</span>));
    tap.ShouldReceiveTouch = (recognizer, touch) =&gt; !(touch.View <span class="hljs-keyword">is</span> UIControl);

    view.AddGestureRecognizer(tap);

    <span class="hljs-keyword">return</span> tap;
}
</code></pre>
<p>Back in our <em>FormViewController</em>:</p>
<p><code>View.DismissKeyboardOnTap();</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698360422716/364a1f38-db59-494c-a876-075d2db50782.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-reacting-to-keyboard-events">Reacting to keyboard events</h3>
<pre><code class="lang-csharp">_willHideObserver = NSNotificationCenter.DefaultCenter
    .AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification);

_willShowObserver = NSNotificationCenter.DefaultCenter
    .AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification);
</code></pre>
<p>That´s all you need to react to the keyboard showing up or hiding in iOS. Next, we´ll move/animate the content accordingly:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnKeyboardNotification</span>(<span class="hljs-params">NSNotification notification</span>)</span>
{
    <span class="hljs-keyword">if</span> (!_controller.IsViewLoaded) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">//Check if the keyboard is becoming visible</span>
    <span class="hljs-keyword">var</span> visible = notification.Name == UIKeyboard.WillShowNotification;

    <span class="hljs-comment">//Start an animation, using values from the keyboard</span>
    UIView.BeginAnimations(<span class="hljs-string">"FollowKeyboard"</span>);
    UIView.SetAnimationBeginsFromCurrentState(<span class="hljs-literal">true</span>);
    UIView.SetAnimationDuration(UIKeyboard.AnimationDurationFromNotification(notification));
    UIView.SetAnimationCurve((UIViewAnimationCurve)UIKeyboard.AnimationCurveFromNotification(notification));

    <span class="hljs-comment">//Pass the notification, calculating keyboard height, etc.</span>
    <span class="hljs-keyword">var</span> landscape = _controller.InterfaceOrientation == UIInterfaceOrientation.LandscapeLeft
                    || _controller.InterfaceOrientation == UIInterfaceOrientation.LandscapeRight;

    <span class="hljs-keyword">var</span> keyboardFrame = visible
        ? UIKeyboard.FrameEndFromNotification(notification)
        : UIKeyboard.FrameBeginFromNotification(notification);

    OnKeyboardChanged(visible, landscape ? keyboardFrame.Width : keyboardFrame.Height);

    <span class="hljs-comment">//Commit the animation</span>
    UIView.CommitAnimations();
}

<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnKeyboardChanged</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> visible, nfloat keyboardHeight</span>)</span>
{
    <span class="hljs-keyword">var</span> activeView = _controller.View.FindFirstResponder();
    <span class="hljs-keyword">var</span> scrollView = activeView?.FindSuperviewOfType(_controller.View, <span class="hljs-keyword">typeof</span>(UIScrollView)) <span class="hljs-keyword">as</span> UIScrollView;

    <span class="hljs-keyword">if</span> (scrollView == <span class="hljs-literal">null</span>)
        <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">if</span> (!visible)
    {
        scrollView.ContentInset = UIEdgeInsets.Zero;
        scrollView.ScrollIndicatorInsets = UIEdgeInsets.Zero;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">var</span> contentInsets = <span class="hljs-keyword">new</span> UIEdgeInsets(<span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>, keyboardHeight, <span class="hljs-number">0.0f</span>);
        scrollView.ContentInset = contentInsets;
        scrollView.ScrollIndicatorInsets = contentInsets;

        <span class="hljs-comment">// Position of the active field relative isnside the scroll view</span>
        <span class="hljs-keyword">var</span> relativeFrame = activeView.Superview.ConvertRectToView(activeView.Frame, scrollView);

        <span class="hljs-keyword">var</span> landscape = _controller.InterfaceOrientation == UIInterfaceOrientation.LandscapeLeft
                        || _controller.InterfaceOrientation == UIInterfaceOrientation.LandscapeRight;

        <span class="hljs-keyword">var</span> spaceAboveKeyboard = (landscape ? scrollView.Frame.Width : scrollView.Frame.Height) - keyboardHeight;

        <span class="hljs-comment">// Move the active field to the center of the available space</span>
        <span class="hljs-keyword">var</span> offset = relativeFrame.Y - (spaceAboveKeyboard - activeView.Frame.Height) / <span class="hljs-number">2</span>;
        scrollView.ContentOffset = <span class="hljs-keyword">new</span> CGPoint(<span class="hljs-number">0</span>, offset);
    }
}
</code></pre>
<p>The previous code is adapted from a great snippet found <a target="_blank" href="https://forums.xamarin.com/discussion/comment/23235#Comment_23235">here</a>. I put it all together in a single reusable class called <code>AutoScrollHelper</code> to use in any <code>UIViewController</code> by composition:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">private</span> UITapGestureRecognizer _gesture;
<span class="hljs-keyword">private</span> AutoScrollHelper _autoScrollHelper;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ViewWillAppear</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> animated</span>)</span>
{
    <span class="hljs-keyword">base</span>.ViewWillAppear(animated);

    _gesture = View.DismissKeyboardOnTap();
    _autoScrollHelper = <span class="hljs-keyword">new</span> AutoScrollHelper(<span class="hljs-keyword">this</span>);
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ViewWillDisappear</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> animated</span>)</span>
{
    <span class="hljs-keyword">base</span>.ViewWillDisappear(animated);

    _gesture.Dispose();
    _gesture = <span class="hljs-literal">null</span>;

    _autoScrollHelper.Dispose();
    _autoScrollHelper = <span class="hljs-literal">null</span>;
}
</code></pre>
<p>Now any focused control will be always visible:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698360355486/e804d095-0ca9-4e20-8a92-b10af3a9b738.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-using-tags-and-the-nextdone-button">Using tags and the Next/Done button</h3>
<p>A user can simply tap the next control when she is done editing the current one, but a good practice is enabling the “Next” key of the keyboard to do it automatically. This will be faster and improve usability.</p>
<p>Setting the property <code>ReturnKeyType</code> of a <code>UITextField</code> we can change the keyboard “intro” key to:</p>
<ul>
<li><p><code>UIReturnKeyType.Next</code></p>
</li>
<li><p><code>UIReturnKeyType.Done</code></p>
</li>
<li><p><code>UIReturnKeyType.Send</code></p>
</li>
<li><p><code>UIReturnKeyType.Search</code></p>
</li>
<li><p>etc</p>
</li>
</ul>
<p>We are interested in Next and Done actions, so we will set “Next” for all text fields except the last one, which will be set to “Done”:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> count = <span class="hljs-number">7</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">1</span>; i &lt;= count; i++)
{
    _contentView.Add(<span class="hljs-keyword">new</span> UITextField
    {
        Placeholder = <span class="hljs-string">$"Test <span class="hljs-subst">{i}</span>"</span>,
        BorderStyle = UITextBorderStyle.RoundedRect,
        Tag = i, <span class="hljs-comment">// useful for ShouldReturn delegate</span>
        ReturnKeyType = i &lt; count 
            ? UIReturnKeyType.Send 
            : UIReturnKeyType.Done
    });
}
</code></pre>
<p>Subscribe to the <code>ShouldReturn</code> delegate on every <code>UITextField</code>:</p>
<p><code>textField.ShouldReturn = ShouldReturn;</code></p>
<p>Now we need to change the focus to the next control when the “Next” key is pressed and hide the keyboard when the “Done” key is pressed on the last <code>UITextField</code>. Additionally, the “Done” key may invoke the <code>Save()</code> method if you like:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">ShouldReturn</span>(<span class="hljs-params">UITextField textField</span>)</span>
{
    <span class="hljs-keyword">if</span> (textField.ReturnKeyType == UIReturnKeyType.Done)
    {
        <span class="hljs-comment">// we are done, hide the keyboard</span>
        View.EndEditing(<span class="hljs-literal">true</span>);

        <span class="hljs-comment">// nothing else to edit, why not just saving the form?</span>
        Save();

        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }

    <span class="hljs-keyword">var</span> nextTag = textField.Tag + <span class="hljs-number">1</span>;
    UIResponder nextControl = _contentView.ViewWithTag(nextTag);

    <span class="hljs-keyword">if</span> (nextControl != <span class="hljs-literal">null</span>)
    {
        <span class="hljs-comment">// set focus on the next control</span>
        nextControl.BecomeFirstResponder();
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-comment">// Not found, hide keyboard.</span>
        View.EndEditing(<span class="hljs-literal">true</span>);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698360286766/f4ec77d3-450b-4bf2-a5be-6fa2f21690fe.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>I think usability is sometimes underappreciated and it can make a big difference for the end user if we care about these kinds of details.</p>
<p>I focused on the obvious in this post, but a lot more can be done. It depends on the type of form and the type of controls we are dealing with.</p>
<p>Grab the <a target="_blank" href="https://github.com/xleon/AutoScroll">complete source code at GitHub</a></p>
]]></content:encoded></item><item><title><![CDATA[Easy and cross-platform localization (Xamarin & .NET)]]></title><description><![CDATA[Sooner or later we all need to localize an app in multiple languages. Each platform provides its way of localizing strings and you may wonder how to share locale files between them.
If you are a .NET dev, Resx is probably the first thing crossing you...]]></description><link>https://xleon.net/easy-and-cross-platform-localization-xamarin-net</link><guid isPermaLink="true">https://xleon.net/easy-and-cross-platform-localization-xamarin-net</guid><category><![CDATA[Xamarin]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[i18n]]></category><dc:creator><![CDATA[Diego Ponce de León]]></dc:creator><pubDate>Wed, 08 Feb 2017 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ywqa9IZB-dU/upload/ca0ca5b229907dd7c4be0d3ef3cb55e0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sooner or later we all need to localize an app in multiple languages. Each platform provides its way of localizing strings and you may wonder how to share locale files between them.</p>
<p>If you are a .NET dev, <a target="_blank" href="https://msdn.microsoft.com/en-us/library/ekyft91f(v=vs.80).aspx">Resx</a> is probably the first thing crossing your mind, and that´s fine for Visual Studio. It can be implemented on <a target="_blank" href="https://developer.xamarin.com/guides/xamarin-forms/advanced/localization/">Xamarin</a> as well but you´ll find yourself (and potentially your client) editing XML files with the following format:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Key"</span> <span class="hljs-attr">xml:space</span>=<span class="hljs-string">"preserve"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">value</span>&gt;</span>KeyValue<span class="hljs-tag">&lt;/<span class="hljs-name">value</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">comment</span>&gt;</span>Some comments<span class="hljs-tag">&lt;/<span class="hljs-name">comment</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">data</span>&gt;</span>
</code></pre>
<p>At least at the time of this writing Xamarin does not provide a Resx editor. Besides, you need to write code for manually loading the correct language, based normally on the current system culture. Xamarin has a <a target="_blank" href="https://developer.xamarin.com/guides/xamarin-forms/advanced/localization/">nice tutorial</a> on how to do it. You´ll find even iOS, Android and Win phone concrete implementations and a bunch of useful details. They also explain how to automatically bind translations in XAML creating a <code>IMarkupExtension</code> class.</p>
<p>The problem is:</p>
<ul>
<li><p>I don´t want to repeat all that process in every project, or even copy-paste across projects and platforms.</p>
</li>
<li><p>I just want a library to do it for me</p>
</li>
<li><p>I don´t like/want Resx files and I don´t want to have a client editing them</p>
</li>
</ul>
<h2 id="heading-hello-i18n-portable"><strong>Hello I18N-Portable</strong></h2>
<p>I love simplicity and that´s why I created a lightweight utility for this matter. My idea was based on two concepts:</p>
<ul>
<li><p>Simpler locale files (similar to java .properties format)</p>
</li>
<li><p>Straightforward setup and initialization</p>
</li>
</ul>
<h3 id="heading-install"><strong>Install</strong></h3>
<p>I18N-Portable is a <a target="_blank" href="https://www.nuget.org/packages/I18NPortable/">Nuget package</a></p>
<h3 id="heading-locales"><strong>Locales</strong></h3>
<p>A locale is just a <code>[locale].txt</code> file with <code>key=value</code> pairs. You place these files on a PCL project once, under a directory called “Locales”. They will be shared across platforms:</p>
<pre><code class="lang-csharp"><span class="hljs-meta"># this is a comment</span>
key1 = one
key2 = two
key3 = translation with \nmultiple \nlines
</code></pre>
<h3 id="heading-setup"><strong>Setup</strong></h3>
<pre><code class="lang-csharp">I18N.Current
    .SetFallbackLocale(<span class="hljs-string">"en"</span>)
    .Init(GetType().GetTypeInfo().Assembly);
</code></pre>
<p>That´s all you need. Simple enough?</p>
<p><em>You put this line at your PCL code, normally in any method executed during app initialization.</em></p>
<p>What just happen?</p>
<p>The <code>Init()</code> method tells I18N-Portable which assembly is hosting the locales. Now the library figures out the current system culture and tries to load a matching locale. If you didn´t provide a locale for the current culture, it will fall back to English (<code>“en.txt”</code>).</p>
<p>From now on you can get any translation with a string method extension from anywhere in your app PCL and/or platform projects:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> translation = <span class="hljs-string">"key1"</span>.Translate(); <span class="hljs-comment">// one</span>
</code></pre>
<h3 id="heading-can-i-use-this-with-my-awesome-mvvm-framework"><strong>Can I use this with my awesome Mvvm framework?</strong></h3>
<p>Sure. You can bind any text view to a particular key, from XAML (Xamarin.Forms, UWP, etc) and classic Android/iOS if your Mvvm framework implements bindings:</p>
<p>XAML:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">Content</span>=<span class="hljs-string">"{Binding [key]}"</span> /&gt;</span>
</code></pre>
<p>Xamarin.Forms XAML:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">Text</span>=<span class="hljs-string">"{Binding [key]}"</span> /&gt;</span>
</code></pre>
<p>Android with MvvmCross:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">TextView</span> <span class="hljs-attr">local:MvxBind</span>=<span class="hljs-string">"Text [key]"</span> /&gt;</span>
</code></pre>
<p>iOS with MvvmCross:</p>
<pre><code class="lang-csharp">bindingSet.Bind(view).To(<span class="hljs-string">"[key]"</span>);
</code></pre>
<p>To make this work, a simple indexer is needed in your ViewModel (I usually do this in a BaseViewModel once):</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-keyword">this</span>[<span class="hljs-keyword">string</span> key] =&gt; key.Translate();
</code></pre>
<p>More handy stuff and details are available at the <a target="_blank" href="https://github.com/xleon/I18N-Portable">GitHub repo</a></p>
<p>Cheers!</p>
]]></content:encoded></item></channel></rss>