Showing posts with label iOS. Show all posts
Showing posts with label iOS. Show all posts

Tuesday, June 18, 2019

Generate iOS InfoPlist.strings Translations from Google Sheets

In my previous article Generating ResX translations from Google Sheets, I wrote about using Google Sheets as a translation tool by using the GOOGLETRANSLATE built in function to generate translation files for a Xamarin.Forms solution. For this post, I will demonstrate something very similar, but instead of ResX files I'll generate InfoPlist.strings files in iOS for localizing the permission request prompts for accessing things like Camera, Location, Photo Gallery, etc. For the sake of this article I created this sample Google Sheets

For a quick recap, we will use a tool called csvtrans written by my colleague and good friend, Ricky Kaare Engelharth. The tool is built with .NET Core and can be installed using this command

dotnet tool install -g csvtrans

Using the tool is also straight forward and it also comes with some quick start instructions

USAGE: csvtrans [--help] [--sheet <document id> <sheet name>]
                [--csv <url or path>] [--format <apple|android|resx>]
                [--outputdir <directory path>] [--name <string>]
                [--convert-placeholders <regex pattern>]

OPTIONS:

    --sheet, -s <document id> <sheet name>
                          specify a Google Sheet as input.
    --csv, -c <url or path>
                          specify a online or local cvs file as input.
    --format, -f <apple|android|resx>
                          specify the output format.
    --outputdir, -o <directory path>
                          specify the output directory.
    --name, -n <string>   specify an optional name for the output.
    --convert-placeholders, -p <regex pattern>
                          convert placeholders to match the output format.
    --help                display this list of options.

Here’s an example usage of the tool

csvtrans --sheet 125id155PUq-6Odwg8Nf9fmkgBsKahTGbJYaYBD2rpSg iOS --format apple --outputdir .\Resources --name InfoPlist

The first argument –-sheet is the Google Sheet document ID followed by the Sheet Name, the next argument –-format specifies the output file format, the argument –-outputdir specifies the output folder, and the last argument --name specifies the output filename.

You can get the Document ID from the URL of the Google Sheet



Here's an example output



Now I can just bring these files into my project and use them directly. Well, almost! There's one little problem, and that is that by default the Xamarin.iOS csproj tooling explicitly adds each InfoPlist.strings file as a BundleResource. Oddly enough, the csproj format allows to specify wild card folders, so if we want to enable dynamic generation of InfoPlist.strings translations then we need to manually edit the csproj.

This is actually very easy to do. We just need to replace the lines like



with



This opens up for dynamic translations at build time using your CI/CD build tools of choice

Sunday, June 9, 2019

Scrollable UISegmentedControl for Xamarin.iOS

A few years ago, I had a full time job as a device developer in the Music Streaming industry. The applications we produced at the time targeted consumers and had a huge focus on UX and UI. One of the requirements our designers had was to have scrollable tabs. This was 5 years ago and before Xamarin.Forms existed so we built the iOS app and Android app separately. We used MvvmCross and the shared a lot of core code but the UI components were done per OS. When we started, the company did a recent switch to go full on with .NET on everything, so not only did we share code between apps, but we shared code across all systems within the entire organization.

Scrollable tabs come for free in Android using the built-in control TabLayout but on iOS we needed to re-create the UISegmentedControl and add scroll/pan/swipe functionality to it. On native code, you had a few options to choose from, so the first task was to find the best native implementation of it and port it to C#. A few Google searches later I found the HMSegmentedControl written by Hesham Abd-Elmegid. The component at the time was written in a single file and was a drop in replacement for the UISegmentedControl. It was functional, elegent, and directly portable to C#. It was perfect!

A few hours of focused coding later I managed to port the entire thing to C# and created a Github project for it. I originally called it HMSegmentedControl as a tribute to the author (I also sent him a thank you email at the time) but later changed it to ScrollableSegmentedControl as it was a better and more descriptive name that states exactly what it does. Recently, I re-visited this project to clean up, modernize the code, and structure of the repository. I added a README file with a useful description, screenshots, and code examples. I also published a NuGet package called ScrollableSegmentedControl to make it easier for others to use while keeping the responsibility of maintaining it.

So using the component is quite trivial. Here's all you need to do:

Add the ScrollableSegmentedControl NuGet package



then you create an instance of ScrollableSegmentedControl and you add it to a View

using System;
using ChristianHelle.Controls.iOS;
using CoreGraphics;
using UIKit;

namespace ScrollableSegmentedControlSample
{
    public partial class ViewController : UIViewController
    {
        public ViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            CreateScrollableSegmentedControl();
        }

        private void CreateScrollableSegmentedControl()
        {
            var sectionTitles = new[] { "One", "Two", "Three", "Four", "Five", "Six" };
            View.AddSubview(new ScrollableSegmentedControl(sectionTitles)
            {
                Font = UIFont.FromName("STHeitiSC-Light", 18.0f),
                Frame = new CGRect(0, 60, View.Frame.Width, 40),
                SegmentEdgeInset = new UIEdgeInsets(0, 10, 0, 10),
                SelectionStyle = ScrollableSegmentedControlSelectionStyle.FullWidthStripe,
                SelectionIndicatorLocation = ScrollableSegmentedControlIndicatorLocation.Down
            });
        }
    }
}

This would result in a segmented control that looks like this:



and can look like one of these examples depending on the SelectionStyle and SelectionIndicatorLocation



This post is probably 5 years too late but since I just only recently made it publicly available as a NuGet package I thought I should write a short article about it. I hope you find this useful