Coming back to wanting to build the iOS version of an application, I ran into an issue where the application crashed on startup. Before this the following occurred :-
Visual Studio 2019 on the Mac was upgraded
Visual Studio 2017 on the PC no longer built the application
Wanting to downgrade what was on the Mac in terms of Xamarin.iOS version
Visual Studio 2019 on the PC was installed
iPhone 7 was upgraded to iOS 13
From Debugging, it seemed to be related to prompting for permission of the Location services, which sent me down a certain path, which then lead me to the following thread :-
A post on this thread by sschmidTU pointed me in the direction of viewing the console from the iOS device via XCode :-
"Also, to get console output for your crash on a release version on device:
connect your device to your Mac
open XCode
go to Window -> Devices and Simulators
click Open Console for your device
in the Console, type your app name in the search window to filter the output."
Having filtered the console output using the application name, I found that the application was crashing because the Bluetooth Permission was being prompted for however I didn't have the NSBluetoothAlwaysUsageDescription key in my Info.plist.
Looking at the documentation for this key, it is new in iOS 13 :-
In summary, ensure the appropriate Mono.Android.dll version is copied to the \bin\Debug folder for your project and then the various details will be picked up from the AssemblyInfo.cs, including the Version number.
We had a requirement to create .app packages of our Xamarin Forms application for submission to a third party. The requirement was to create this for a iPhone 6 iOS 10.2 Simulator.
Initially I thought rebuilding the solution in Visual Studio 2015 would be enough as this would create what looked like the .app package however when using the following command line :-
This resulted in a "Failed to chmod : No such file or directory" error.
To resolve this issue I found that along with rebuilding the solution in Visual Studio, you need to deploy the application to the simulator, which inflates the .app package file by ~50MB, and on inspection of the before and after .app packages, the DLLs and EXE are included which allows the .app package file to be deployed to the simulator successfully, using the command line above.
You may have a requirement to bind a string property to a Color property, such as TextColor. If this is the case then you will need to create a converter to perform the conversion from string to Color. The following is a sample of such a converter :-
Firstly, the converter :-
using System;
using System.Globalization;
using Xamarin.Forms;
namespace LabelTextColorSample
{
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string valueAsString = value.ToString();
switch (valueAsString)
{
case (""):
{
return Color.Default;
}
case ("Accent"):
{
return Color.Accent;
}
default:
{
return Color.FromHex(value.ToString());
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
Then the XAML which consumes it :-
Then the setting of the BindingContext :-
using Xamarin.Forms;
namespace LabelTextColorSample
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = MyViewModel.Instance;
}
}
}
And finally the ViewModel :-
namespace LabelTextColorSample
{
public class MyViewModel
{
private static MyViewModel _instance;
public static MyViewModel Instance
{
get { return _instance ?? (_instance = new MyViewModel()); }
}
public string MyTextColor
{
get { return "#00FF00"; }
}
}
}
I normally use a device for all my debugging in Xamarin however I thought I would try the Xamarin Android Player.
After installing and downloading an emulator image I tried starting the emulator however I was present with the following error :-
OpenGL server is unreachable. Please check that Xamarin Android Player is allowed through your firewall on public networks.
As I use McAfee, my firewall settings are handled by McAfee rather than the Windows Firewall. I viewed the Firewall settings for the Xamarin Android Player (AndroidPlayer) under "View firewall and anti-spam settings > Firewall > Internet Connections for Programs", and it was set to use "Designated Ports". Editing this entry and setting Incoming and Outgoing to "Open ports to Work and Home networks" now allows me to use the Xamarin Android Player.
The following shows how to set a Color to a HSLA value using a XAML Extension.
Firstly, define a new class called ColorAsHslaExtension.cs :-
[ContentProperty("ColorAsHsla")]
public class ColorAsHslaExtension : IMarkupExtension
{
public string ColorAsHsla { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
var elements = ColorAsHsla.Split(',');
double h = double.Parse(elements[0]);
double s = double.Parse(elements[1]);
double l = double.Parse(elements[2]);
double a = double.Parse(elements[3]);
return Color.FromHsla(h, s, l, a);
}
}
Next, define the XAML that you make use of the Extension :-
This sample was written in classic Xamarin.Android, however I was looking for a Xamarin.Forms implementation. This sample however provided me with the ground work for the sample I propose below.
My sample is only targeting Android at the moment.
My implementation consists of a ViewCellRenderer, this allows you to define a ListView with an ItemTemplate, so that you can bind your ListView to more than a List.
MyViewCellRenderer.cs :-
using System.Collections;
using Android.Content;
using Android.Views;
using ListViewDragDropSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(ViewCell), typeof(MyViewCellRenderer))]
namespace ListViewDragDropSample.Droid
{
public class MyViewCellRenderer : ViewCellRenderer
{
public ListView ParentListView { get; set; }
public IList Items { get; set; }
protected override View GetCellCore(Cell item, View convertView, ViewGroup parent, Context context)
{
ParentListView = item.ParentView as ListView;
if (ParentListView != null)
{
Items = ParentListView.ItemsSource as IList;
}
var cellcore = base.GetCellCore(item, convertView, parent, context);
cellcore.Drag -= CellcoreOnDrag;
cellcore.Drag += CellcoreOnDrag;
return cellcore;
}
private void CellcoreOnDrag(object sender, View.DragEventArgs args)
{
ViewGroup = sender as ViewGroup;
if (ViewGroup != null)
{
ListView = ViewGroup.Parent.Parent as Android.Widget.ListView;
}
switch (args.Event.Action)
{
case DragAction.Started:
args.Handled = true;
break;
case DragAction.Entered:
args.Handled = true;
if (ListView != null)
{
if (FirstIndex == -1)
{
FirstIndex = ListView.IndexOfChild(ViewGroup.Parent as View);
}
}
break;
case DragAction.Exited:
args.Handled = true;
break;
case DragAction.Drop:
args.Handled = true;
if (SecondIndex == -1)
{
SecondIndex = ListView.IndexOfChild(ViewGroup.Parent as View);
}
if (FirstIndex != -1)
{
var firstItem = Items[FirstIndex];
if (firstItem != null)
{
Items.RemoveAt(FirstIndex);
Items.Insert(SecondIndex, firstItem);
ParentListView.ItemsSource = null;
ParentListView.ItemsSource = Items;
}
}
FirstIndex = -1;
SecondIndex = -1;
break;
case DragAction.Ended:
args.Handled = true;
break;
}
}
public Android.Widget.ListView ListView { get; set; }
public ViewGroup ViewGroup { get; set; }
private static int _firstIndex = -1;
private static int _secondIndex = -1;
public static int FirstIndex
{
get { return _firstIndex; }
set { _firstIndex = value; }
}
public static int SecondIndex
{
get { return _secondIndex; }
set { _secondIndex = value; }
}
}
}
MyListViewRenderer.cs :-
using Android.Content;
using ListViewDragDropSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ListView), typeof(MyListViewRenderer))]
namespace ListViewDragDropSample.Droid
{
public class MyListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
Control.ItemLongClick += (s, args) =>
{
ClipData data = ClipData.NewPlainText("List", args.Position.ToString());
MyDragShadowBuilder myShadownScreen = new MyDragShadowBuilder(args.View);
args.View.StartDrag(data, myShadownScreen, null, 0);
};
}
}
}
MainPage.xaml :-
MainPage.xaml.cs :-
using System.Collections.Generic;
using Xamarin.Forms;
namespace ListViewDragDropSample
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
Items = new List();
for (int i = 1; i < 11; i++)
{
Items.Add(new Item()
{
Title = "Title : " + i,
Description = "Description : " + i,
});
}
BindingContext = this;
}
public List Items { get; set; }
}
}
MyDragShadowBuilder.cs :-
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
namespace ListViewDragDropSample.Droid
{
public class MyDragShadowBuilder : View.DragShadowBuilder
{
private Drawable shadow;
public MyDragShadowBuilder(View v)
: base(v)
{
v.DrawingCacheEnabled = true;
Bitmap bm = v.DrawingCache;
shadow = new BitmapDrawable(bm);
shadow.SetColorFilter(Color.ParseColor("#4EB1FB"), PorterDuff.Mode.Multiply);
}
public override void OnProvideShadowMetrics(Point size, Point touch)
{
int width = View.Width;
int height = View.Height;
shadow.SetBounds(0, 0, width, height);
size.Set(width, height);
touch.Set(width / 2, height / 2);
}
public override void OnDrawShadow(Canvas canvas)
{
base.OnDrawShadow(canvas);
shadow.Draw(canvas);
}
}
}
And finally the Item.cs :-
namespace ListViewDragDropSample
{
public class Item
{
public string Title { get; set; }
public string Description { get; set; }
}
}
As of version 1.3.5-pre1 of Xamarin.Forms, there doesn't seem to be a way to swipe a TabbedPage on Android out of the box.
I first thought I could add the gesture via a GestureListener in a Custom Renderer, however this causes issues when trying to swipe while showing a full screen button or ListView, as these two controls consume the Gestures, so this wasn't an option.
I then had an idea about combining a CarouselPage and TabbedPage, hence this post.
This is very convoluted and will not perform very well when used in complex UI layouts, but it might be useful.
First the XAML :-
As you can see this layout won't work for Windows Phone and I haven't tested this on iOS however I am only really targeting Android at the moment.
Next is the C# :-
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
AttachCurrentPageChanged();
}
private void AttachCurrentPageChanged()
{
Page1.CurrentPageChanged += MultiPage_OnCurrentPageChanged;
Page2.CurrentPageChanged += MultiPage_OnCurrentPageChanged;
Page3.CurrentPageChanged += MultiPage_OnCurrentPageChanged;
}
private void DetachCurrentPageChanged()
{
Page1.CurrentPageChanged -= MultiPage_OnCurrentPageChanged;
Page2.CurrentPageChanged -= MultiPage_OnCurrentPageChanged;
Page3.CurrentPageChanged -= MultiPage_OnCurrentPageChanged;
}
private void MultiPage_OnCurrentPageChanged(object sender, EventArgs e)
{
DetachCurrentPageChanged();
CarouselPage carouselPage = sender as CarouselPage;
if (carouselPage != null)
{
int indexOf = carouselPage.Children.IndexOf(carouselPage.CurrentPage);
var tabbedPage = carouselPage.ParentView as TabbedPage;
if (tabbedPage != null)
{
tabbedPage.CurrentPage = tabbedPage.Children[indexOf];
var newCarouselPage = tabbedPage.CurrentPage as CarouselPage;
if (newCarouselPage != null)
{
newCarouselPage.CurrentPage = newCarouselPage.Children[indexOf];
}
}
}
AttachCurrentPageChanged();
}
}
The OnCurrentPageChanged is used to track the Page Change of the CarouselPage, the index of this page is then used to set the CurrentPage of the next CarouselPage.
As this tracking needs to occur the number of child Pages in each of the CarouselPages needs to equal the numbwe of Tabs you would like to show.
If you want to add more Tabs, you add more CarouselPage's as children of the TabbedPage and then ensure that each CarouselPage is updated with the correct number of children.
This is by far an ideal solution but was interesting nevertheless.
I was investigating how to call a REST web service using an Authorization header from jQuery but was running into a cross domain issue. I found the following blog post which detailed changes to the Global.asax :-
I have been working on a new feature of an application that is displaying some information on a custom Live tile. The source of the image used for the tile is stored in the Shared/ShellContent directory of the application's Isolated Storage. The application also has the ability to clear it's Isolated Storage for resetting purposes.
The issue then came about when the reset was performed this cleared the whole of the Isolated Storage including the Shared directory. The code I am using to then create the PNG in Isolated Storage failed with a "Operation not permitted on IsolatedStorageFileStream." exception.
The Shared directory seems to be created when the application is first deployed to the device so my Live tile was working fine until the reset was performed.
So if you are using a PNG from Isolated Storage for your Live tile, please make sure you don't remove the entire Isolated Storage for the application.
I used the Isolated Storage Explorer Tool to investigate this problem :-
I added the PerformanceProgressBar and set IsIndeterminate to True, I then let the PerformanceProgressBar run for two cycles, the associated graph is as follows :-
As you can see, the Storyboard starts as soon as the application starts.
The CPU usage increases as the PerformanceProgressBar animates, it then reduces for around a second and then starts again.
You can also see the Frame rate increase when the PerformanceProgressBar is animating.
Could a performance issue be "masked" when using the PerformanceProgressBar? Or would we see a spike in the CPU usage?
If you have any experiences to share with using the Performance Analysis Tool, I would be very interested to hear them.
My next observation is based on adding a button to the same solution as previously created, then implement a DispatcherTimer to increment a Count property, this Count property is data bound to the Content property of the Button control. Tapping the button starts the DispatcherTimer, the Dipatcher time then increments the Count property every 250ms, determining if the Count has reached 10, if it has then the DispatcherTimer.Stop method is called.
The associated graph is as follows :-
As you can see there is a regular frame rate increase as the Button Content is updated.
Also worth noting is the Start of a Storyboard which is inline with the tapping of the Button.
If you have any experiences to share with using the Performance Analysis Tool, I would be very interested to hear them.
My second observation is related to the Keyboard appearing and disappearing.
To the project I created in the previous post, I have added a TextBox control.
Here is the graph produced :-
As you can see the CPU usage relating to the startup and shutdown of the application still remains however now we have a section in the middle relating to the showing of the keyboard (applying focus to the textbox).
The showing of the keyboard has forced the Frame rate to shoot up, however this seems to be normal in the other applications I have tested.
If you have any experiences to share with using the Performance Analysis Tool, I would be very interested to hear them.