Corrado's Blog 2.0

Online thoughts of a technology funatic

Fixing SDWebImage issue on Xamarin.iOS

Loading an image into an ImageView control using Xamarin.iOS is a straightforward process:

  • Add a ImageView to a ViewController and give it a name (e.g: MyImage)
  • Add an image to Resources folder (e.g Users.png) and be sure that related Build Action is set to BundleResource.
  • And add this code:
        public override async void ViewDidLoad() { base.ViewDidLoad(); MyImage.Image=UIImage.FromBundle("Users.png"); }

      And you’re done, as alternative you can also use UIImage.FromFile, in this case the call is asynchronous and doesn’t use caching, while former is synchronous and does caching.

      Ok, but what if I want to show an image from a web url? things gets more complicated since you have to do the entire downloading process manually, something like:

      public async Task<UIImage> FromUrl(string imageUrl) { var httpClient = new HttpClient(); var contents = await httpClient.GetByteArrayAsync(imageUrl); return UIImage.LoadFromData(NSData.FromArray(contents)); }

      And use it this way:

      MyImage.Image = await this.FromUrl("http://thechromenews.com/wp-content/uploads/2015/11/Apple-Logo-4.png");

      Cool, but what if I want to use caching, show a placeholder or fire some code when downloading completes? well, that’s more code to write and since this is a quite common task you’ll be happy to know that there’s a component that does that for you.

      Look for SDWebImage on Xamarin Component Store and add it to your project:

      image

      Doing that adds to ImageView control a SetImage extension method that accepts a Uri and does all the work for you, usage is very easy, just write something like:

      NSUrl url=new NSUrl("http://thechromenews.com/wp-content/uploads/2015/11/Apple-Logo-4.png"); MyImage.SetImage(url,null,null);

      If you try this code you’ll discover that it doesn’t work, why? is the component buggy? well no, if you try it on older iOS versions it works fine.

      Also this quite common alternative raises an exception on iOS 9.x

      private UIImage FromUri(string uri) { using (var url = new NSUrl(uri)) using (var data = NSData.FromUrl(url)) return UIImage.LoadFromData(data); }

      Ok, so what is the problem? well Apple decided that starting from iOS 9.0 all communications must use ATS (app transport security) and if you want to change this behavior you have to add an entry into info.plist file.

      This blog post describes ATS in detail and I encourage you to read it: http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/

      To cut a long story short: Add this entry to your info.plist

      <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>

      And everything will work as expected. Smile

      HTH

      Change ListView RowHeight programmatically in Xamarin Android

      Here’s a quick code snippet that allows you to change the row height of an Android ListView in Xamarin

      public override View GetView(int position, View convertView, ViewGroup parent) { View view = convertView ?? this.context.LayoutInflater.Inflate(Android.Resource.Layout.ActivityListItem, null); if (view.LayoutParameters == null) { view.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, 300); } else { view.LayoutParameters.Height = 300; } ...other code here... return view; }

      Code resides inside ListView adapter, as you see, the magic is acting on view LayoutParameters property.

      Hope it helps.

      Genymotion issue with Windows 10 10586

      I use Genymotion for my Xamarin Android development, I know that both Xamarin and Microsoft provide their own emulator but, for my machine configuration Genymotion is the one I like most at the moment.

      This post is to help you find a solution in case the virtual device doesn’t boot on a machine after the upgrade to TH2 aka build 10586, with this message:

      image

      obviously the message is meaningless, digging into device log file located at %LocalAppData%\Genymobile\Genymotion\deployed\  I’ve discovered that error depends on a network issue:

      0:00:01.362237 VMSetError: Failed to open/create the internal network ‘HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter #4′

      After doing several test and internet search I finally found the reason of the problem: no idea why but an option in the adapter settings was not selected:

      image

      The option is: VirtualBox NDIS6 Bridged Networking adapter, if not selected, select it and try again, your emulator should now work (or at least, it worked for me)

      works-on-my-machine-starburst

       

      Hope it works for you too, otherwise good luck finding the solution, if you find it please share, Genymotion support for free licenses is far than optimal.

      Adding a splash screen to Xamarin Forms apps

      Every Xamarin Forms app should ship with a splash screen, it is the first thing a user see and it contributes to reduce the perceived application boot time.

      Adding one is quite easy, let’s go from simpler to harder.

      The simpler: Windows Phone

      Just replace SplashScreenImage.jpg (720×1280 pixels) with your own app launch image in the Windows Phone project and you’re done.

      image

      The harder: iOS

      If you follow iOS documentation you’ll know that adding your own launch screen is just a matter of adding the proper images to the project via info.pList options.

      image

      unfortunately once you do that you’ll still see the blue background with Xamarin Logo at startup, to fix it delete the content of Launch Storyboard option into info.pList and you’re done (no idea why template adds that storyboard inside Resources folder)

      image

      The hardest: Android

      Adding a splash screen requires following steps:

      image

      1-Add the splash screen image into Resources\drawable folder (and all the variants you want to support) in previous screenshot file is named splash.png

      2-Create a new folder under Resources and name it values

      3-Add an xml file into values folder (name is not important, in my case I named it Styles.xml)

      4-Add this content to xml file

      <?xml version="1.0" encoding="utf-8" ?>
      <resources>
          <style name="Theme.Splash" parent="android:Theme">
              <item name="android:windowBackground">@drawable/splash</item>
              <item name="android:windowNoTitle">true</item>
          </style>
      </resources>

      5-Add a new activity to the project and add this code (of course you can safely remove the delay before StartActivity method)

      [Activity(Theme = "@style/Theme.Splash", //Indicates the theme to use for this activity
                   MainLauncher = true, //Set it as boot activity
                   NoHistory = true)] //Doesn't place it in back stack
          public class SplashActivity : Activity
          {
              protected override void OnCreate(Bundle bundle)
              {
                  base.OnCreate(bundle);
                  System.Threading.Thread.Sleep(3000); //Let's wait awhile...
                  this.StartActivity(typeof(MainActivity));
              }
          }

      6-Remove the MainLauncher=true from original activity since this is the new activity that will be launched at startup.

      Hope this helps…

      Application wide resources (reloaded)

      Back in September 2014 I blogged about adding application wide resources to Xamarin Forms applications, that technique is no longer needed since starting from v 1.3 Xamarin added support to app wide resources into Xamarin Forms core.

      So what it this post about? It is about adding (and using) them in a more user-friendly way.

      Since v 1.3 you can create a resource that can be reachable from all pages this way:

      public class App : Application
          {
              public App()
              {
                  this.Resources = new ResourceDictionary
                  {
                      {"TextColor", Color.FromHex("00FF00")}
                  };
      
                  // The root page of your application
                  this.MainPage = new MainView();
              }
          }

      And use the resource from a XAML page this way:

      <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                   x:Class="App1.MainView">
          <Label Text="Hello from Xamarin Forms" 
                   VerticalOptions="Center" 
                   HorizontalOptions="Center" 
                   TextColor="{StaticResource TextColor}" />
      </ContentPage>

      Ok, It works but is not very friendly, resource name is not listed in XAML Intellisense (powered by Resharper of course) and as XAML lover I’d like to define my application resources in XAML the same way I do in a page.

      Luckily you can and it’s very easy:

      1- Delete App.cs file

      2-Add a new Forms XAML Page and name it App.cs

      image

      3-Make App.cs inherit from Application instead of ContentPage

      public partial class App : Application
          {
              public App()
              {
                  InitializeComponent();
              }
          }

      4-Replace the generated XAML with following one, don’t forget to substitute [YourNamespaceHere] with your own’s application namespace

      <Application xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       x:Class="[YourNamespaceHere].App">
          <Application.Resources>
              <ResourceDictionary>
              </ResourceDictionary>
          </Application.Resources>
      </Application>

      Your project should now look like this:

      image

      and you can add your resources inside ResourceDictionary as in following example:

      <Application xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       x:Class="[YourNamespaceHere].App">
          <Application.Resources>
              <ResourceDictionary>
                  <Color x:Key="TextColor">Red</Color>
              </ResourceDictionary>
          </Application.Resources>
      </Application>

      And now, your resource will work as expected but it will also be listed by Intellisense

      image

      Definitively more friendly than using code (IMHO)

      Hope to see this feature added to Xamarin Forms template soon.

      Visual Studio 2013: Fixing NuGet package install error in Xamarin Android projects

      Recently, quite often indeed, I’m facing an issue trying to add some NuGet packages to a Xamarin Android project, in this particular case I was trying to add Xamarin.Android.Support.RecyclerView package.

      image

      The error I get after clicking Install is:

      image

      Some search on the internet provided me some solutions, the one working for me is changing Android version option from:

      image

      to an explicit value (API 21 in my case)

      image

      after this change, all packages get installed properly.

      Behaviors for Xamarin Forms v2.0

      I’ve pushed version 2.0 of my behavior implementation for Xamarin Forms and moved it to a new location on GitHub: https://github.com/corradocavalli/Corcav.Behaviors

      The reason for this is that, in order to prevent conflict with Xamarin API I’ve renamed the root namespace so upgrading to this version will break your code and I’m really sorry for this, but I’m sure it won’t take too long to fix it.

      I’ve also removed old nuget package and replaced with this one : ’https://www.nuget.org/packages/Corcav.Behaviors/

      Version 2.0 uses Xamarin Forms 1.3.4 and Unified API project as iOS demo.

      Sorry for breaking your code, my fault using Xamarin as assembly name Sad smile (lesson learned), honestly didn’t expect such success, and I thank you for that.

      Get device unique identifier in Xamarin Forms

      Last week I introduced some ISV to the wonderful world of Xamarin and while discussing Xamarin Forms a few of them asked how to get an identifier that uniquely identifies a device.

      I had the same issue in the past with a Windows Phone application so I know how retrieve it on this platform and I remember speaking with the guy that was working on the same app for iOS that he told me that Apple changed the policy so the API that was used in the past was no longer available and he had to find a workaround, more on this later.

      The answer to the original question is quite easy from the architectural side: Just use a Dependency Service and you’re done, here are the steps:

      Let’s create a IDevice interface in the common/shared project and let’s add a GetIdentifier method

      public interface IDevice
       {
        string GetIdentifier();  
       }

      Once done you can easily get device unique identifier within shared code using this snippet:

      IDevice device = DependencyService.Get<IDevice>();
      string deviceIdentifier = device.GetIdentifier();

      Let’s now implement device specific code, starting from Windows Phone, add this class to Windows Phone project and add ID_CAP_IDENTITY_DEVICE capability.

      [assembly: Xamarin.Forms.Dependency(typeof(WinPhoneDevice))]
      namespace XFUniqueIdentifier.WinPhone
      {
       public class WinPhoneDevice : IDevice
       {
        public string GetIdentifier()
        {
         byte[] myDeviceId = (byte[])Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("DeviceUniqueId");
         return Convert.ToBase64String(myDeviceId);
        }
       }
      }

      Now add following class to Android project:

      [assembly: Xamarin.Forms.Dependency(typeof(AndroidDevice))]
      namespace XFUniqueIdentifier.Droid
      {
       public class AndroidDevice : IDevice
       {
        public string GetIdentifier()
        {
         return Settings.Secure.GetString(Forms.Context.ContentResolver, Settings.Secure.AndroidId);
        }
       }
      }

      And now the hardest part: iOS developer used to get device identifier using Device UniqueIdentifier api but this has deprecated by Apple so an alternative has to be found.

      After some investigations (also considered generating unique id at startup but this would fail if app is reinstalled) I found this post on Xamarin forums so kudos goes to the original author.

      It’s now time to add a new class to iOS project with following code:

      [assembly: Xamarin.Forms.Dependency(typeof(IOSDevice))]
      namespace XFUniqueIdentifier.iOS
      {
       using System.IO;
       using System.Runtime.InteropServices;
      
       public class IOSDevice : IDevice
       {
        [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
        private static extern uint IOServiceGetMatchingService(uint masterPort, IntPtr matching);
      
        [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
        private static extern IntPtr IOServiceMatching(string s);
      
        [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
        private static extern IntPtr IORegistryEntryCreateCFProperty(uint entry, IntPtr key, IntPtr allocator, uint options);
      
        [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
        private static extern int IOObjectRelease(uint o);
      
        public string GetIdentifier()
        {
         string serial = string.Empty;
         uint platformExpert = IOServiceGetMatchingService(0, IOServiceMatching("IOPlatformExpertDevice"));
         if (platformExpert != 0)
         {
          NSString key = (NSString) "IOPlatformSerialNumber";
          IntPtr serialNumber = IORegistryEntryCreateCFProperty(platformExpert, key.Handle, IntPtr.Zero, 0);
          if (serialNumber != IntPtr.Zero)
          {
           serial = NSString.FromHandle(serialNumber);
          }
      
          IOObjectRelease(platformExpert);
         }
      
         return serial;
        }  
      }

      And you’re done, have fun and make a good use of device unique id. Smile

      Xamarin build server is too old?

      This morning, no idea why, trying to compile a Xamarin.iOS app resulted in an error “Xamarin build server is tool old…” apparently without any reason since nothing has changed on my machine during the weekend. Tried also connecting to a different Mac machine but error was still there while compiling using Xamarin Studio on the Mac machine was ok.
      After some investigation I’ve found the solution: looks like that “someone” changed my proxy settings (probably Fiddler) and that was causing the weird message.

      Turning it off did the trick.

      image

      Hope this will help you in case you see this weird message too.

      Declarative event handlers in Xamarin Android

      In Android applications the UI components are totally isolated from associated activity so to ‘glue’ them you need to follow a ‘javascript like’ approach: find the element fist  and then interact with it. Let’s say you have a .axml file containing a button with id MyButton

       <Button
              android:id="@+id/MyButton"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:text="@string/Hello" />

      in the associated activity, to subscribe its Click event you have to write this code:

      public class MainActivity : Activity
       {
        int count = 1;
      
        protected override void OnCreate(Bundle bundle)
        {
         base.OnCreate(bundle);
      
         SetContentView(Resource.Layout.Main);
         Button button = FindViewById<Button>(Resource.Id.MyButton);
         button.Click += button_Click;
        }
      
        void button_Click(object sender, EventArgs e)
        {
         (sender as Button).Text = string.Format("{0} clicks!", count++);
        }
       }

      If you, like me, come from XAML and love the declarative approach I’ve got a good new for you, there’s an alternative, just follow this steps:

      1-Add a reference to Mono.Android.Export library

      2-Declare you handler in .axml file this way:

      <Button
              android:id="@+id/MyButton"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:text="@string/Hello"
          android:onClick="myHandler"/>

      3-Refactor the code in associated activity this way:

      public class MainActivity : Activity
       {
        int count = 1;
      
        protected override void OnCreate(Bundle bundle)
        {
         base.OnCreate(bundle);
      
         SetContentView(Resource.Layout.Main);
        }
      
        [Java.Interop.Export("myHandler")]
        public void button_Click(View v)
        {
         (v as Button).Text = string.Format("{0} clicks!", count++);
        }
       }

      And you’re done!

      As you see the trick is to export the function, using the Java.Interop.Export attribute that also allows you to use a export the method with a different name, leave it empty if you don’t need to differentiate.

      Lot simpler, and cleaner… IMHO Smile

      « Newer PostsOlder Posts »