[Enhancement] Implements CharacterSpacing (#5167)
authorksemenenko <KSemenenko@users.noreply.github.com>
Thu, 18 Jul 2019 12:28:23 +0000 (15:28 +0300)
committerStephane Delcroix <stephane@delcroix.org>
Thu, 18 Jul 2019 12:28:23 +0000 (14:28 +0200)
- fixes #4855

* TextElement.LetterSpacingProperty

ButtonRenderer

DatePickerRenderer

EditorRenderer

EntryRenderer.cs

LabelRenderer

PickerRenderer

SearchBarRenderer

TimePickerRenderer

fix renderers

LetterSpacingElement

Extensions ToEm

* iOS

Picker

* UWP

* removal of an unnecessary condition

* Remove ILetterSpacingElement.OnLetterSpacingChanged

Fix for PickerRenderer for Android

* Move LetterSpacing to TextElement

* UWP

* Span

* Span and LetterSpacing

* Tests

* fixes for renderers

* Renderers

* LetterSpacingGallery

* Android fixes

* Extensions

* delete macos code

* iOS and UWP Renderers

* Renderers for iOS

* Picker.Items

* UWP

* fix merge

* Renderers

* iOS renderers

* PickerRenderer.cs

* Material

* Material Renderer

* Picker placeholder

* InvalidateMeasure

* Update Xamarin.Forms.Core/Span.cs

Co-Authored-By: Stephane Delcroix <stephane@delcroix.org>
* Update Xamarin.Forms.Core/ITextElement.cs

Co-Authored-By: Stephane Delcroix <stephane@delcroix.org>
* remove LetterSpacing tests

* style

* Rename LetterSpacing to CharacterSpacing

* Update Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.Android/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.Android/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.Android/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.Android/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialPickerRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialEditorRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialEditorRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* Update Xamarin.Forms.Material.iOS/MaterialButtonRenderer.cs

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
* SearchHandler

* UAP CharacterSpacing

* Update Xamarin.Forms.Core/Shell/SearchHandler.cs

Co-Authored-By: Stephane Delcroix <stephane@delcroix.org>
55 files changed:
Xamarin.Forms.Controls/CoreGallery.cs
Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
Xamarin.Forms.Core/Button.cs
Xamarin.Forms.Core/DatePicker.cs
Xamarin.Forms.Core/Editor.cs
Xamarin.Forms.Core/Entry.cs
Xamarin.Forms.Core/ITextElement.cs
Xamarin.Forms.Core/Label.cs
Xamarin.Forms.Core/Picker.cs
Xamarin.Forms.Core/SearchBar.cs
Xamarin.Forms.Core/Shell/SearchHandler.cs
Xamarin.Forms.Core/Span.cs
Xamarin.Forms.Core/TextElement.cs
Xamarin.Forms.Core/TimePicker.cs
Xamarin.Forms.Material.Android/MaterialButtonRenderer.cs
Xamarin.Forms.Material.Android/MaterialPickerRenderer.cs
Xamarin.Forms.Material.iOS/MaterialButtonRenderer.cs
Xamarin.Forms.Material.iOS/MaterialEditorRenderer.cs
Xamarin.Forms.Material.iOS/MaterialPickerRenderer.cs
Xamarin.Forms.Platform.Android/AppCompat/ButtonRenderer.cs
Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs
Xamarin.Forms.Platform.Android/Extensions.cs
Xamarin.Forms.Platform.Android/FastRenderers/ButtonRenderer.cs
Xamarin.Forms.Platform.Android/FastRenderers/LabelRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/ButtonRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/DatePickerRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/EditorRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/EntryRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/FormattedStringExtensions.cs
Xamarin.Forms.Platform.Android/Renderers/LabelRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/SearchBarRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/TimePickerRenderer.cs
Xamarin.Forms.Platform.UAP/ButtonRenderer.cs
Xamarin.Forms.Platform.UAP/DatePickerRenderer.cs
Xamarin.Forms.Platform.UAP/EditorRenderer.cs
Xamarin.Forms.Platform.UAP/EntryRenderer.cs
Xamarin.Forms.Platform.UAP/Extensions.cs
Xamarin.Forms.Platform.UAP/FormsButton.cs
Xamarin.Forms.Platform.UAP/LabelRenderer.cs
Xamarin.Forms.Platform.UAP/PickerRenderer.cs
Xamarin.Forms.Platform.UAP/SearchBarRenderer.cs
Xamarin.Forms.Platform.UAP/TimePickerRenderer.cs
Xamarin.Forms.Platform.iOS/Extensions/Extensions.cs
Xamarin.Forms.Platform.iOS/Renderers/ButtonRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/DatePickerRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/EditorRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/FormattedStringExtensions.cs
Xamarin.Forms.Platform.iOS/Renderers/LabelRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/PickerRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/SearchBarRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/TimePickerRenderer.cs

index 4e02033..72cc79f 100644 (file)
@@ -295,6 +295,7 @@ namespace Xamarin.Forms.Controls
                                new GalleryPageFactory(() => new PlatformSpecificsGallery(), "Platform Specifics"),
                                new GalleryPageFactory(() => new NativeBindingGalleryPage(), "Native Binding Controls Gallery"),
                                new GalleryPageFactory(() => new XamlNativeViews(), "Xaml Native Views Gallery"),
+                               new GalleryPageFactory(() => new CharacterSpacingGallery(), "CharacterSpacing Views Gallery"),
                                new GalleryPageFactory(() => new AppLinkPageGallery(), "App Link Page Gallery"),
                                new GalleryPageFactory(() => new NestedNativeControlGalleryPage(), "Nested Native Controls Gallery"),
                                new GalleryPageFactory(() => new CellForceUpdateSizeGalleryPage(), "Cell Force Update Size Gallery"),
diff --git a/Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml
new file mode 100644 (file)
index 0000000..fb6b89f
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+             x:Class="Xamarin.Forms.Controls.GalleryPages.CharacterSpacingGallery" Visual="Material">
+
+    <StackLayout>
+        <Label>
+            <Label.FormattedText>
+                <FormattedString>
+                    <Span Text="CharacterSpacing" />
+                    <Span Text=": " />
+                    <Span Text="0" x:Name="CharacterSpacingValue" />
+                </FormattedString>
+            </Label.FormattedText>
+        </Label>
+        <Slider Minimum="-10" Maximum="10" Value="0" ValueChanged="Slider_OnValueChanged" MaximumTrackColor="Gray"
+                MinimumTrackColor="Gray"  Margin="20,0"/>
+
+        <ScrollView>
+            <StackLayout>
+                <Label Text="Welcome to Xamarin.Forms! - Label" CharacterSpacing="0" x:Name="Label" TextColor="Red"/>
+                <Label>
+                    <Label.FormattedText>
+                        <FormattedString>
+                            <Span Text="Welcome to Xamarin.Forms! - Span" CharacterSpacing="0" x:Name="Span" TextColor="Red"/>
+                        </FormattedString>
+                    </Label.FormattedText>
+                </Label>
+                <Entry Text="Welcome to Xamarin.Forms! - Entry" CharacterSpacing="0" x:Name="Entry" TextColor="Red" PlaceholderColor="BlueViolet"/>
+                <Entry Placeholder="Welcome to Xamarin.Forms! - Entry" CharacterSpacing="0" x:Name="PlaceholderEntry" TextColor="Red" PlaceholderColor="BlueViolet"/>
+                <Editor Text="Welcome to Xamarin.Forms! - Editor" CharacterSpacing="0" x:Name="Editor" TextColor="Red" PlaceholderColor="BlueViolet"/>
+                <Editor Placeholder="Welcome to Xamarin.Forms! - Editor" CharacterSpacing="0" x:Name="PlaceholderEditor" TextColor="Red" PlaceholderColor="BlueViolet"/>
+                <DatePicker Date="2019-01-01" CharacterSpacing="0" x:Name="DatePicker" TextColor="Red"/>
+                <TimePicker Time="10:10" CharacterSpacing="0" x:Name="TimePicker" TextColor="Red"/>
+                <Picker Title="Welcome to Xamarin.Forms! - Picker"  CharacterSpacing="0" x:Name="Picker" TextColor="Red" TitleColor="BlueViolet">
+                    <Picker.Items>
+                        <x:String>Welcome to Xamarin.Forms1</x:String>
+                        <x:String>Welcome to Xamarin.Forms2</x:String>
+                    </Picker.Items>
+                </Picker>
+                <SearchBar Text="Welcome to Xamarin.Forms! - SearchBar" CharacterSpacing="0" x:Name="SearchBar" TextColor="Red" PlaceholderColor="BlueViolet"/>
+                <SearchBar Placeholder="Welcome to Xamarin.Forms! - SearchBar" CharacterSpacing="0" x:Name="PlaceholderSearchBar" TextColor="Red" PlaceholderColor="BlueViolet"/>
+                <Button Text="Welcome to Xamarin.Forms! - Button" CharacterSpacing="0" x:Name="Button" TextColor="Red"/>
+            </StackLayout>
+        </ScrollView>
+    </StackLayout>
+
+
+</ContentPage>
diff --git a/Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/CharacterSpacingGallery.xaml.cs
new file mode 100644 (file)
index 0000000..a0772aa
--- /dev/null
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Controls.GalleryPages
+{
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       public partial class CharacterSpacingGallery : ContentPage
+       {
+               public CharacterSpacingGallery()
+               {
+                       InitializeComponent();
+               }
+
+               void Slider_OnValueChanged(object sender, ValueChangedEventArgs e)
+               {
+                       CharacterSpacingValue.Text = e.NewValue.ToString();
+                       Button.CharacterSpacing = e.NewValue;
+                       DatePicker.CharacterSpacing = e.NewValue;
+                       Editor.CharacterSpacing = e.NewValue;
+                       Entry.CharacterSpacing = e.NewValue;
+                       PlaceholderEntry.CharacterSpacing = e.NewValue;
+                       PlaceholderEditor.CharacterSpacing = e.NewValue;
+                       Label.CharacterSpacing = e.NewValue;
+                       Picker.CharacterSpacing = e.NewValue;
+                       SearchBar.CharacterSpacing = e.NewValue;
+                       PlaceholderSearchBar.CharacterSpacing = e.NewValue;
+                       TimePicker.CharacterSpacing = e.NewValue;
+                       Span.CharacterSpacing = e.NewValue;
+               }
+       }
+}
\ No newline at end of file
index 84c40ae..993ccfb 100644 (file)
@@ -44,6 +44,9 @@
     <Compile Update="GalleryPages\CollectionViewGalleries\SelectionGalleries\PreselectedItemsGallery.xaml.cs">
       <DependentUpon>PreselectedItemsGallery.xaml</DependentUpon>
     </Compile>
+    <Compile Update="GalleryPages\CharacterSpacingGallery.xaml.cs">
+      <DependentUpon>CharacterSpacingGallery.xaml</DependentUpon>
+    </Compile>
     <Compile Update="GalleryPages\VisualStateManagerGalleries\OnPlatformExample.xaml.cs">
       <DependentUpon>OnPlatformExample.xaml</DependentUpon>
     </Compile>
@@ -53,6 +56,9 @@
     <EmbeddedResource Update="GalleryPages\BindableLayoutGalleryPage.xaml">
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
+    <EmbeddedResource Update="GalleryPages\CharacterSpacingGallery.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
     <EmbeddedResource Update="GalleryPages\CollectionViewGalleries\EmptyViewGalleries\EmptyViewSwapGallery.xaml">
        <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
index 1e10eea..e5de4b6 100644 (file)
@@ -27,6 +27,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty FontProperty = FontElement.FontProperty;
 
                public static readonly BindableProperty FontFamilyProperty = FontElement.FontFamilyProperty;
@@ -154,6 +156,12 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                bool IButtonElement.IsEnabledCore
                {
                        set { SetValueCore(IsEnabledProperty, value); }
@@ -321,6 +329,12 @@ namespace Xamarin.Forms
                {
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
+
                void IBorderElement.OnBorderColorPropertyChanged(Color oldValue, Color newValue)
                {
                }
index 3c858b6..a5bdba4 100644 (file)
@@ -5,7 +5,7 @@ using Xamarin.Forms.Platform;
 namespace Xamarin.Forms
 {
        [RenderWith(typeof(_DatePickerRenderer))]
-       public class DatePicker : View, IFontElement, ITextElement,IElementConfiguration<DatePicker>
+       public class DatePicker : View, IFontElement, ITextElement, IElementConfiguration<DatePicker>
        {
                public static readonly BindableProperty FormatProperty = BindableProperty.Create(nameof(Format), typeof(string), typeof(DatePicker), "d");
 
@@ -21,7 +21,9 @@ namespace Xamarin.Forms
                        validateValue: ValidateMaximumDate, coerceValue: CoerceMaximumDate);
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
-               
+
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty FontFamilyProperty = FontElement.FontFamilyProperty;
 
                public static readonly BindableProperty FontSizeProperty = FontElement.FontSizeProperty;
@@ -65,6 +67,12 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                public FontAttributes FontAttributes
                {
                        get { return (FontAttributes)GetValue(FontAttributesProperty); }
@@ -162,5 +170,11 @@ namespace Xamarin.Forms
                void ITextElement.OnTextColorPropertyChanged(Color oldValue, Color newValue)
                {
                }
+
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
        }
 }
\ No newline at end of file
index 6ddf34b..b88d571 100644 (file)
@@ -19,6 +19,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty PlaceholderProperty = PlaceholderElement.PlaceholderProperty;
 
                public static readonly BindableProperty PlaceholderColorProperty = PlaceholderElement.PlaceholderColorProperty;
@@ -48,6 +50,12 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                public string Placeholder {
                        get => (string)GetValue(PlaceholderElement.PlaceholderProperty);
                        set => SetValue(PlaceholderElement.PlaceholderProperty, value);
@@ -136,6 +144,12 @@ namespace Xamarin.Forms
                {
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
+
                private static void OnTextChanged(Editor bindable, string oldValue, string newValue)
                {
                        bindable.TextChanged?.Invoke(bindable, new TextChangedEventArgs(oldValue, newValue));
index 64ae405..6c97f9a 100644 (file)
@@ -25,6 +25,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty HorizontalTextAlignmentProperty = TextAlignmentElement.HorizontalTextAlignmentProperty;
 
                public static readonly BindableProperty FontFamilyProperty = FontElement.FontFamilyProperty;
@@ -82,6 +84,12 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                public FontAttributes FontAttributes
                {
                        get { return (FontAttributes)GetValue(FontAttributesProperty); }
@@ -186,6 +194,12 @@ namespace Xamarin.Forms
                {
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
+
                void ITextAlignmentElement.OnHorizontalTextAlignmentPropertyChanged(TextAlignment oldValue, TextAlignment newValue)
                {
                }
index 8f5cc87..20a384d 100644 (file)
@@ -9,5 +9,10 @@ namespace Xamarin.Forms
 
                //note to implementor: but implement this method explicitly
                void OnTextColorPropertyChanged(Color oldValue, Color newValue);
+
+               double CharacterSpacing { get; }
+
+               //note to implementor: but implement these methods explicitly
+               void OnCharacterSpacingPropertyChanged(double oldValue, double newValue);
        }
-}
\ No newline at end of file
+}
index 5fbbae1..50dcded 100644 (file)
@@ -28,6 +28,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty FontProperty = FontElement.FontProperty;
 
                public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(Label), default(string), propertyChanged: OnTextPropertyChanged);
@@ -140,6 +142,11 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
                public TextAlignment VerticalTextAlignment
                {
                        get { return (TextAlignment)GetValue(VerticalTextAlignmentProperty); }
@@ -344,6 +351,12 @@ namespace Xamarin.Forms
                {
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
+
                public override IList<GestureElement> GetChildElements(Point point)
                {
                        if (FormattedText?.Spans == null || FormattedText?.Spans.Count == 0)
index 39f2070..56f10fd 100644 (file)
@@ -15,6 +15,8 @@ namespace Xamarin.Forms
        {
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty TitleProperty =
                        BindableProperty.Create(nameof(Title), typeof(string), typeof(Picker), default(string));
 
@@ -100,12 +102,20 @@ namespace Xamarin.Forms
                        set { SetValue(SelectedItemProperty, value); }
                }
 
-               public Color TextColor {
+               public Color TextColor
+               {
                        get { return (Color)GetValue(TextElement.TextColorProperty); }
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
-               public string Title {
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
+               public string Title
+               {
                        get { return (string)GetValue(TitleProperty); }
                        set { SetValue(TitleProperty, value); }
                }
@@ -274,5 +284,11 @@ namespace Xamarin.Forms
                void ITextElement.OnTextColorPropertyChanged(Color oldValue, Color newValue)
                {
                }
+
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
        }
 }
\ No newline at end of file
index 2fe3eac..c182bc3 100644 (file)
@@ -38,6 +38,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                readonly Lazy<PlatformConfigurationRegistry<SearchBar>> _platformConfigurationRegistry;
 
                public Color CancelButtonColor
@@ -86,6 +88,12 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                bool IsEnabledCore
                {
                        set { SetValueCore(IsEnabledProperty, value); }
@@ -188,6 +196,11 @@ namespace Xamarin.Forms
                {
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
                void ITextAlignmentElement.OnHorizontalTextAlignmentPropertyChanged(TextAlignment oldValue, TextAlignment newValue)
                {
                }
index 6482b99..f9f9a74 100644 (file)
@@ -108,9 +108,7 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
-               void ITextElement.OnTextColorPropertyChanged(Color oldValue, Color newValue)
-               {
-               }
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
 
                public Color TextColor
                {
@@ -149,6 +147,12 @@ namespace Xamarin.Forms
                        set { SetValue(FontFamilyProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                [TypeConverter(typeof(FontSizeConverter))]
                public double FontSize
                {
@@ -504,6 +508,16 @@ namespace Xamarin.Forms
                        ((SearchHandler)bindable).OnCommandParameterChanged();
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       
+               }
+
+               void ITextElement.OnTextColorPropertyChanged(Color oldValue, Color newValue)
+               {
+
+               }
+
                static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
                {
                        var self = (SearchHandler)bindable;
index 6962b08..ab691f3 100644 (file)
@@ -42,6 +42,14 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                [Obsolete("Foreground is obsolete as of version 3.1.0. Please use the TextColor property instead.")]
                [EditorBrowsable(EditorBrowsableState.Never)]
                public static readonly BindableProperty ForegroundColorProperty = TextColorProperty;
@@ -141,6 +149,10 @@ namespace Xamarin.Forms
                {
                }
 
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+               }
+
                internal override void ValidateGesture(IGestureRecognizer gesture)
                {
                        switch (gesture)
index d1d7152..a11156f 100644 (file)
@@ -6,9 +6,19 @@ namespace Xamarin.Forms
                        BindableProperty.Create(nameof(ITextElement.TextColor), typeof(Color), typeof(ITextElement), Color.Default,
                                                                        propertyChanged: OnTextColorPropertyChanged);
 
+               public static readonly BindableProperty CharacterSpacingProperty =
+                       BindableProperty.Create(nameof(ITextElement.CharacterSpacing), typeof(double), typeof(ITextElement), 0.0d,
+                               propertyChanged: OnCharacterSpacingPropertyChanged);
+
                static void OnTextColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
                {
                        ((ITextElement)bindable).OnTextColorPropertyChanged((Color)oldValue, (Color)newValue);
                }
+
+               static void OnCharacterSpacingPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+               {
+                       ((ITextElement)bindable).OnCharacterSpacingPropertyChanged((double)oldValue, (double)newValue);
+               }
+
        }
 }
\ No newline at end of file
index 65506d3..108b7fd 100644 (file)
@@ -11,6 +11,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
 
+               public static readonly BindableProperty CharacterSpacingProperty = TextElement.CharacterSpacingProperty;
+
                public static readonly BindableProperty TimeProperty = BindableProperty.Create(nameof(Time), typeof(TimeSpan), typeof(TimePicker), new TimeSpan(0), BindingMode.TwoWay, (bindable, value) =>
                {
                        var time = (TimeSpan)value;
@@ -42,6 +44,12 @@ namespace Xamarin.Forms
                        set { SetValue(TextElement.TextColorProperty, value); }
                }
 
+               public double CharacterSpacing
+               {
+                       get { return (double)GetValue(TextElement.CharacterSpacingProperty); }
+                       set { SetValue(TextElement.CharacterSpacingProperty, value); }
+               }
+
                public TimeSpan Time
                {
                        get { return (TimeSpan)GetValue(TimeProperty); }
@@ -90,5 +98,11 @@ namespace Xamarin.Forms
                void ITextElement.OnTextColorPropertyChanged(Color oldValue, Color newValue)
                {
                }
+
+               void ITextElement.OnCharacterSpacingPropertyChanged(double oldValue, double newValue)
+               {
+                       InvalidateMeasure();
+               }
+
        }
 }
\ No newline at end of file
index 6dbd3c6..6b21ff0 100644 (file)
@@ -170,6 +170,8 @@ namespace Xamarin.Forms.Material.Android
                                UpdatePrimaryColors();
                        else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
                                UpdateInputTransparent();
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
 
                        ElementPropertyChanged?.Invoke(this, e);
                }
@@ -287,6 +289,11 @@ namespace Xamarin.Forms.Material.Android
                        ViewCompat.SetBackgroundTintList(this, MaterialColors.CreateButtonBackgroundColors(background));
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       LetterSpacing = Element.CharacterSpacing.ToEm();
+               }
+
                IPlatformElementConfiguration<PlatformConfiguration.Android, Button> OnThisPlatform() =>
                        _platformElementConfiguration ?? (_platformElementConfiguration = Element.OnThisPlatform());
 
index ba21d12..aa675db 100644 (file)
@@ -46,7 +46,10 @@ namespace Xamarin.Forms.Material.Android
                        _textInputLayout.BoxBackgroundColor = MaterialColors.CreateEntryFilledInputBackgroundColor(Element.BackgroundColor, Element.TextColor);
                }
 
-               protected override void UpdatePlaceHolderText() => _textInputLayout.SetHint(string.Empty, Element);
+               protected override void UpdatePlaceHolderText()
+               {
+                       _textInputLayout.SetHint(Element.Title, Element);
+               }
                protected override void UpdateTitleColor() => ApplyTheme();
                protected override void UpdateTextColor() => ApplyTheme();
                protected virtual void ApplyTheme() => _textInputLayout?.ApplyTheme(Element.TextColor, Color.Default);
index eda3e9b..0afa482 100644 (file)
@@ -1,6 +1,7 @@
 using System;
 using System.ComponentModel;
 using CoreGraphics;
+using Foundation;
 using MaterialComponents;
 using UIKit;
 using Xamarin.Forms.Platform.iOS;
@@ -76,6 +77,7 @@ namespace Xamarin.Forms.Material.iOS
                                UpdateTextColor();
                                _buttonLayoutManager?.Update();
                                ApplyTheme();
+                               UpdateCharacterSpacing();
                        }
                }
 
@@ -111,6 +113,10 @@ namespace Xamarin.Forms.Material.iOS
                        {
                                UpdateBorder();
                        }
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                       {
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == Button.CornerRadiusProperty.PropertyName)
                        {
                                UpdateCornerRadius();
@@ -224,6 +230,13 @@ namespace Xamarin.Forms.Material.iOS
                        }
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       var attributedString = new NSMutableAttributedString(Element.Text ?? string.Empty).AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+                       Control.SetAttributedTitle(attributedString, UIControlState.Normal);
+                       Control.SetAttributedTitle(attributedString, UIControlState.Highlighted);
+                       Control.SetAttributedTitle(attributedString, UIControlState.Disabled);
+               }
                void UpdateTextColor()
                {
                        if (_buttonScheme.ColorScheme is SemanticColorScheme colorScheme)
index 283f96b..64836b6 100644 (file)
@@ -1,6 +1,7 @@
 using UIKit;
 using MaterialComponents;
 using System;
+using Foundation;
 using Xamarin.Forms.Platform.iOS;
 
 namespace Xamarin.Forms.Material.iOS
@@ -39,6 +40,11 @@ namespace Xamarin.Forms.Material.iOS
                        Control.UpdatePlaceholder(this);
                }
 
+               protected internal override void UpdateCharacterSpacing()
+               {
+                       Control.AttributedText = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+               }
+
                protected internal override void UpdateText()
                {
                        if (!_hackHasRan)
@@ -91,4 +97,4 @@ namespace Xamarin.Forms.Material.iOS
                Color IMaterialEntryRenderer.PlaceholderColor => Element?.PlaceholderColor ?? Color.Default;
                Color IMaterialEntryRenderer.BackgroundColor => Element?.BackgroundColor ?? Color.Default;
        }
-}
\ No newline at end of file
+}
index a969842..6962b66 100644 (file)
@@ -23,6 +23,7 @@ namespace Xamarin.Forms.Material.iOS
                {
                        base.OnElementChanged(e);
                        UpdatePlaceholder();
+                       UpdateCharacterSpacing();
                }
 
                string IMaterialEntryRenderer.Placeholder => string.Empty;
@@ -30,4 +31,4 @@ namespace Xamarin.Forms.Material.iOS
                Color IMaterialEntryRenderer.TextColor => Element?.TextColor ?? Color.Default;
                Color IMaterialEntryRenderer.BackgroundColor => Element?.BackgroundColor ?? Color.Default;
        }
-}
\ No newline at end of file
+}
index 30b70e2..705a881 100644 (file)
@@ -126,6 +126,8 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                                UpdateEnabled();
                        else if (e.PropertyName == Button.FontProperty.PropertyName)
                                UpdateFont();
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
 
                        base.OnElementPropertyChanged(sender, e);
                }
@@ -144,6 +146,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                        UpdateTextColor();
                        UpdateEnabled();
                        UpdateBackgroundColor();
+                       UpdateCharacterSpacing();
                }
 
                void UpdateEnabled()
@@ -182,6 +185,15 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                        _textColorSwitcher?.UpdateTextColor(Control, Element.TextColor);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               NativeButton.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+                       
+               }
+
                void IOnClickListener.OnClick(AView v) => ButtonElementManager.OnClick(Element, Element, v);
 
                bool IOnTouchListener.OnTouch(AView v, MotionEvent e) => ButtonElementManager.OnTouch(Element, Element, v, e);
index 0ae6200..017f1c3 100644 (file)
@@ -66,6 +66,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                                UpdateFont();
                                UpdatePicker();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
                        }
 
                        base.OnElementChanged(e);
@@ -79,6 +80,8 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                                UpdatePicker();
                        else if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
                                UpdatePicker();
+                       else if (e.PropertyName == Picker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Picker.TextColorProperty.PropertyName)
                                UpdateTextColor();
                        else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName || e.PropertyName == Picker.FontSizeProperty.PropertyName)
@@ -151,6 +154,14 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                        EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
                }
 
+               protected void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               EditText.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdatePicker()
                {
                        UpdatePlaceHolderText();
@@ -201,6 +212,9 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                        _textColorSwitcher = _textColorSwitcher ?? new TextColorSwitcher(EditText.TextColors, Element.UseLegacyColorManagement());
                        _textColorSwitcher.UpdateTextColor(EditText, Element.TextColor);
                }
-               protected override void UpdatePlaceHolderText() => EditText.Hint = Element.Title;
+               protected override void UpdatePlaceHolderText()
+               {
+                       EditText.Hint = Element.Title;
+               }
        }
 }
\ No newline at end of file
index 3c9b33e..c1703fb 100644 (file)
@@ -41,5 +41,11 @@ namespace Xamarin.Forms.Platform.Android
                internal static bool IsHorizontal(this Button.ButtonContentLayout layout) =>
                        layout.Position == Button.ButtonContentLayout.ImagePosition.Left ||
                        layout.Position == Button.ButtonContentLayout.ImagePosition.Right;
+
+
+               internal static float ToEm(this double pt)
+               {
+                       return (float)pt * 0.0624f; //Coefficient for converting Pt to Em
+               }
        }
 }
\ No newline at end of file
index 9c79ded..c973540 100644 (file)
@@ -207,6 +207,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                                UpdateTextColor();
                                UpdateInputTransparent();
                                UpdateBackgroundColor();
+                               UpdateCharacterSpacing();
                                _buttonLayoutManager?.Update();
 
                                ElevationHelper.SetElevation(this, e.NewElement);
@@ -225,6 +226,10 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                        {
                                UpdateFont();
                        }
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                       {
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
                        {
                                UpdateInputTransparent();
@@ -325,6 +330,14 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                        _textColorSwitcher.Value.UpdateTextColor(this, Button.TextColor);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               LetterSpacing = Button.CharacterSpacing.ToEm();
+                       }
+               }
+
                float IBorderVisualElementRenderer.ShadowRadius => ShadowRadius;
                float IBorderVisualElementRenderer.ShadowDx => ShadowDx;
                float IBorderVisualElementRenderer.ShadowDy => ShadowDy;
index 7e78c15..af058b0 100644 (file)
@@ -7,6 +7,7 @@ using Android.Support.V4.View;
 using Android.Text;
 using Android.Util;
 using Android.Views;
+using Android.Widget;
 using AView = Android.Views.View;
 
 namespace Xamarin.Forms.Platform.Android.FastRenderers
@@ -242,6 +243,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                                SkipNextInvalidate();
                                UpdateText();
                                UpdateLineHeight();
+                               UpdateCharacterSpacing();
                                UpdateTextDecorations();
                                if (e.OldElement?.LineBreakMode != e.NewElement.LineBreakMode)
                                        UpdateLineBreakMode();
@@ -267,6 +269,8 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                                UpdateText();
                        else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName)
                                UpdateLineBreakMode();
+                       else if (e.PropertyName == Label.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Label.TextDecorationsProperty.PropertyName)
                                UpdateTextDecorations();
                        else if (e.PropertyName == Label.TextProperty.PropertyName || e.PropertyName == Label.FormattedTextProperty.PropertyName)
@@ -340,6 +344,14 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                        _lastSizeRequest = null;
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdateLineBreakMode()
                {
                        this.SetLineBreakMode(Element);
index 2190ec6..1356e0b 100644 (file)
@@ -134,6 +134,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateText();
                        else if (e.PropertyName == Button.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
                                UpdateEnabled();
                        else if (e.PropertyName == Button.FontProperty.PropertyName)
@@ -162,6 +164,7 @@ namespace Xamarin.Forms.Platform.Android
                        UpdateText();
                        UpdateBitmap();
                        UpdateTextColor();
+                       UpdateCharacterSpacing();
                        UpdateEnabled();
                        UpdateBackgroundColor();
                        UpdatePadding();
@@ -272,6 +275,14 @@ namespace Xamarin.Forms.Platform.Android
                        _textColorSwitcher?.UpdateTextColor(Control, Element.TextColor);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               Control.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                float IBorderVisualElementRenderer.ShadowRadius => Control.ShadowRadius;
                float IBorderVisualElementRenderer.ShadowDx => Control.ShadowDx;
                float IBorderVisualElementRenderer.ShadowDy => Control.ShadowDy;
index cce0a5b..3680fbf 100644 (file)
@@ -71,6 +71,7 @@ namespace Xamarin.Forms.Platform.Android
                        UpdateMinimumDate();
                        UpdateMaximumDate();
                        UpdateTextColor();
+                       UpdateCharacterSpacing();
                }
 
                protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -85,6 +86,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateMaximumDate();
                        else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == DatePicker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == DatePicker.FontAttributesProperty.PropertyName || e.PropertyName == DatePicker.FontFamilyProperty.PropertyName || e.PropertyName == DatePicker.FontSizeProperty.PropertyName)
                                UpdateFont();
                }
@@ -170,6 +173,14 @@ namespace Xamarin.Forms.Platform.Android
                        EditText.Text = date.ToString(Element.Format);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               EditText.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdateFont()
                {
                        EditText.Typeface = Element.ToTypeface();
index c716a6d..71c4434 100644 (file)
@@ -132,6 +132,7 @@ namespace Xamarin.Forms.Platform.Android
                        UpdateText();
                        UpdateInputType();
                        UpdateTextColor();
+                       UpdateCharacterSpacing();
                        UpdateFont();
                        UpdateMaxLength();
                        UpdatePlaceholderColor();
@@ -151,6 +152,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateInputType();
                        else if (e.PropertyName == Editor.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == Editor.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName)
@@ -236,6 +239,14 @@ namespace Xamarin.Forms.Platform.Android
                        }
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               EditText.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdateText()
                {
                        string newText = Element.Text ?? "";
index dfc5230..36c54bb 100644 (file)
@@ -177,8 +177,8 @@ namespace Xamarin.Forms.Platform.Android
                        UpdatePlaceHolderText();
                        EditText.Text = Element.Text;
                        UpdateInputType();
-
                        UpdateColor();
+                       UpdateCharacterSpacing();
                        UpdateAlignment();
                        UpdateFont();
                        UpdatePlaceholderColor();
@@ -243,6 +243,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateInputType();
                        else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName)
                                UpdateAlignment();
+                       else if (e.PropertyName == Entry.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Entry.FontAttributesProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == Entry.FontFamilyProperty.PropertyName)
@@ -369,6 +371,14 @@ namespace Xamarin.Forms.Platform.Android
                                EditText.Text = currentControlText.Substring(0, Element.MaxLength);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               EditText.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdateReturnType()
                {
                        if (Control == null || Element == null)
index ce49515..6df5c01 100644 (file)
@@ -60,28 +60,35 @@ namespace Xamarin.Forms.Platform.Android
                                }
                                if (!span.IsDefault())
 #pragma warning disable 618 // We will need to update this when .Font goes away
-                                       spannable.SetSpan(new FontSpan(span.Font, view), start, end, SpanTypes.InclusiveInclusive);
+                                       spannable.SetSpan(new FontSpan(span.Font, view, span.CharacterSpacing.ToEm()), start, end, SpanTypes.InclusiveInclusive);
 #pragma warning restore 618
                                else
-                                       spannable.SetSpan(new FontSpan(defaultFont, view), start, end, SpanTypes.InclusiveInclusive);
+                                       spannable.SetSpan(new FontSpan(defaultFont, view, span.CharacterSpacing.ToEm()), start, end, SpanTypes.InclusiveInclusive);
                                if (span.IsSet(Span.TextDecorationsProperty))
                                        spannable.SetSpan(new TextDecorationSpan(span), start, end, SpanTypes.InclusiveInclusive);
+
                        }
                        return spannable;
                }
 
                class FontSpan : MetricAffectingSpan
                {
-                       public FontSpan(Font font, TextView view)
+                       public FontSpan(Font font, TextView view, float characterSpacing)
                        {
                                Font = font;
                                TextView = view;
+                               if (Forms.IsLollipopOrNewer)
+                               {
+                                       CharacterSpacing = characterSpacing;
+                               }
                        }
 
                        public Font Font { get; }
 
                        public TextView TextView { get; }
 
+                       public float CharacterSpacing { get; }
+
                        public override void UpdateDrawState(TextPaint tp)
                        {
                                Apply(tp);
@@ -97,6 +104,10 @@ namespace Xamarin.Forms.Platform.Android
                                paint.SetTypeface(Font.ToTypeface());
                                float value = Font.ToScaledPixel();
                                paint.TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, value, TextView.Resources.DisplayMetrics);
+                               if (Forms.IsLollipopOrNewer)
+                               {
+                                       paint.LetterSpacing = CharacterSpacing;
+                               }
                        }
                }
 
index f29b742..2771697 100644 (file)
@@ -118,6 +118,7 @@ namespace Xamarin.Forms.Platform.Android
                        {
                                UpdateText();
                                UpdateLineBreakMode();
+                               UpdateCharacterSpacing();
                                UpdateLineHeight();
                                UpdateGravity();
                                UpdateMaxLines();
@@ -132,6 +133,9 @@ namespace Xamarin.Forms.Platform.Android
                                        UpdateGravity();
                                if (e.OldElement.MaxLines != e.NewElement.MaxLines)
                                        UpdateMaxLines();
+                               if (e.OldElement.CharacterSpacing != e.NewElement.CharacterSpacing)
+                                       UpdateCharacterSpacing();
+
                        }
                        UpdateTextDecorations();
                        UpdatePadding();
@@ -148,6 +152,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateText();
                        else if (e.PropertyName == Label.FontProperty.PropertyName)
                                UpdateText();
+                       else if (e.PropertyName == Label.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName)
                                UpdateLineBreakMode();
                        else if (e.PropertyName == Label.TextDecorationsProperty.PropertyName)
@@ -228,6 +234,14 @@ namespace Xamarin.Forms.Platform.Android
                        _view.SetLineBreakMode(Element);
                        _lastSizeRequest = null;
                }
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer && Control is TextView textControl)
+                       {
+                               textControl.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
 
                void UpdateLineHeight()
                {
index 26d25d6..e881e47 100644 (file)
@@ -80,6 +80,7 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateFont();
                                UpdatePicker();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
                        }
 
                        base.OnElementChanged(e);
@@ -93,6 +94,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdatePicker();
                        else if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
                                UpdatePicker();
+                       else if (e.PropertyName == Picker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Picker.TextColorProperty.PropertyName)
                                UpdateTextColor();
                        else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName || e.PropertyName == Picker.FontSizeProperty.PropertyName)
@@ -185,6 +188,14 @@ namespace Xamarin.Forms.Platform.Android
                        UpdatePicker();
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               Control.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdateFont()
                {
                        Control.Typeface = Element.ToTypeface();
index 51ff637..88a1a9e 100644 (file)
@@ -113,6 +113,7 @@ namespace Xamarin.Forms.Platform.Android
                        UpdateFont();
                        UpdateAlignment();
                        UpdateTextColor();
+                       UpdateCharacterSpacing();
                        UpdatePlaceholderColor();
                        UpdateMaxLength();
 
@@ -139,6 +140,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateFont();
                        else if (e.PropertyName == SearchBar.FontFamilyProperty.PropertyName)
                                UpdateFont();
+                       else if (e.PropertyName == SearchBar.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == SearchBar.FontSizeProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName)
@@ -248,6 +251,19 @@ namespace Xamarin.Forms.Platform.Android
                                Control.SetQuery(Element.Text, false);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       if(!Forms.IsLollipopOrNewer)
+                               return;
+
+                       _editText = _editText ?? Control.GetChildrenOfType<EditText>().FirstOrDefault();
+
+                       if (_editText != null)
+                       {
+                               _editText.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                void UpdateTextColor()
                {
                        _textColorSwitcher?.UpdateTextColor(_editText, Element.TextColor);
index d0ed9fc..c0d663c 100644 (file)
@@ -63,6 +63,7 @@ namespace Xamarin.Forms.Platform.Android
 
                        SetTime(e.NewElement.Time);
                        UpdateTextColor();
+                       UpdateCharacterSpacing();
                        UpdateFont();
 
                        if ((int)Build.VERSION.SdkInt > 16)
@@ -77,6 +78,8 @@ namespace Xamarin.Forms.Platform.Android
                                SetTime(Element.Time);
                        else if (e.PropertyName == TimePicker.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == TimePicker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == TimePicker.FontAttributesProperty.PropertyName || e.PropertyName == TimePicker.FontFamilyProperty.PropertyName || e.PropertyName == TimePicker.FontSizeProperty.PropertyName)
                                UpdateFont();
                }
@@ -141,7 +144,15 @@ namespace Xamarin.Forms.Platform.Android
                        EditText.Typeface = Element.ToTypeface();
                        EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
                }
-               
+
+               void UpdateCharacterSpacing()
+               {
+                       if (Forms.IsLollipopOrNewer)
+                       {
+                               EditText.LetterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+               }
+
                abstract protected void UpdateTextColor();
        }
 
@@ -168,6 +179,7 @@ namespace Xamarin.Forms.Platform.Android
                        _textColorSwitcher = _textColorSwitcher ?? new TextColorSwitcher(EditText.TextColors, Element.UseLegacyColorManagement());
                        _textColorSwitcher.UpdateTextColor(EditText, Element.TextColor);
                }
-       }
+
+    }
 
 }
index 38160ad..f7b9b2a 100644 (file)
@@ -49,6 +49,9 @@ namespace Xamarin.Forms.Platform.UWP
                                if (Element.IsSet(Button.BorderColorProperty) && Element.BorderColor != (Color)Button.BorderColorProperty.DefaultValue)
                                        UpdateBorderColor();
 
+                               if (Element.IsSet(Button.CharacterSpacingProperty))
+                                       UpdateCharacterSpacing();
+
                                if (Element.IsSet(Button.BorderWidthProperty) && Element.BorderWidth != (double)Button.BorderWidthProperty.DefaultValue)
                                        UpdateBorderWidth();
 
@@ -83,6 +86,10 @@ namespace Xamarin.Forms.Platform.UWP
                        {
                                UpdateContent();
                        }
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                       {
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
                        {
                                UpdateBackground();
@@ -154,6 +161,11 @@ namespace Xamarin.Forms.Platform.UWP
                        Control.BorderThickness = Element.BorderWidth == (double)Button.BorderWidthProperty.DefaultValue ? new WThickness(3) : new WThickness(Element.BorderWidth);
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.UpdateCharacterSpacing(Element.CharacterSpacing.ToEm());
+               }
+
                async void UpdateContent()
                {
                        var text = Element.Text;
index ee2d6e7..9902583 100644 (file)
@@ -1,8 +1,10 @@
 using System;
 using System.ComponentModel;
 using System.Linq;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Documents;
 using Windows.UI.Xaml.Media;
 using Xamarin.Forms.Internals;
 
@@ -45,6 +47,7 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateMaximumDate();
                                UpdateDate(e.NewElement.Date);
                                UpdateFlowDirection();
+                               UpdateCharacterSpacing();
                        }
 
                        base.OnElementChanged(e);
@@ -88,6 +91,8 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateMinimumDate();
                        else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == DatePicker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateFlowDirection();
                        else if (e.PropertyName == DatePicker.FontAttributesProperty.PropertyName || e.PropertyName == DatePicker.FontFamilyProperty.PropertyName || e.PropertyName == DatePicker.FontSizeProperty.PropertyName)
@@ -126,7 +131,12 @@ namespace Xamarin.Forms.Platform.UWP
                {
                        Control.UpdateFlowDirection(Element);
                }
-               
+
+               void UpdateCharacterSpacing()
+               {
+                       Control.CharacterSpacing = Element.CharacterSpacing.ToEm();
+               }
+
                void UpdateFont()
                {
                        if (Control == null)
index 624c15a..67a12bf 100644 (file)
@@ -1,5 +1,6 @@
 using System;
 using System.ComponentModel;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Media;
@@ -55,6 +56,7 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateText();
                                UpdateInputScope();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
                                UpdateFont();
                                UpdateTextAlignment();
                                UpdateFlowDirection();
@@ -115,6 +117,10 @@ namespace Xamarin.Forms.Platform.UWP
                        {
                                UpdateText();
                        }
+                       else if (e.PropertyName == Editor.CharacterSpacingProperty.PropertyName)
+                       {
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                        {
                                UpdateTextAlignment();
@@ -302,6 +308,10 @@ namespace Xamarin.Forms.Platform.UWP
                        Control.InputScope = editor.Keyboard.ToInputScope();
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.CharacterSpacing = Element.CharacterSpacing.ToEm();
+               }
                void UpdateText()
                {
                        string newText = Element.Text ?? "";
index cbe8513..bc77353 100644 (file)
@@ -1,6 +1,7 @@
 using System;
 using System.ComponentModel;
 using Windows.System;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Input;
@@ -56,6 +57,7 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdatePlaceholder();
                                UpdateTextColor();
                                UpdateFont();
+                               UpdateCharacterSpacing();
                                UpdateAlignment();
                                UpdatePlaceholderColor();
                                UpdateMaxLength();
@@ -109,6 +111,10 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdatePlaceholder();
                        else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == Entry.CharacterSpacingProperty.PropertyName)
+                       {
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == InputView.KeyboardProperty.PropertyName)
                                UpdateInputScope();
                        else if (e.PropertyName == InputView.IsSpellCheckEnabledProperty.PropertyName)
@@ -217,6 +223,11 @@ namespace Xamarin.Forms.Platform.UWP
                        _fontApplied = true;
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.CharacterSpacing = Element.CharacterSpacing.ToEm();
+               }
+
                void UpdateInputScope()
                {
                        Entry entry = Element;
index b4ad96e..c5b804d 100644 (file)
@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
 using System.Threading;
 using System.Threading.Tasks;
 using Windows.Foundation;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Input;
@@ -92,5 +93,11 @@ namespace Xamarin.Forms.Platform.UWP
                                return max;
                        return value;
                }
+
+
+               internal static int ToEm(this double pt)
+               {
+                       return Convert.ToInt32( pt * 0.0624f * 1000); //Coefficient for converting Pt to Em. The value is uniform spacing between characters, in units of 1/1000 of an em.
+               }
        }
 }
\ No newline at end of file
index adb4526..4ac4bf0 100644 (file)
@@ -1,4 +1,6 @@
-using Windows.UI.Xaml;
+using System.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Media;
 
 using WContentPresenter = Windows.UI.Xaml.Controls.ContentPresenter;
@@ -77,5 +79,30 @@ namespace Xamarin.Forms.Platform.UWP
                                _contentPresenter.CornerRadius = new Windows.UI.Xaml.CornerRadius(radius);
                        }
                }
+
+               public void UpdateCharacterSpacing(int characterSpacing)
+               {
+                       CharacterSpacing = characterSpacing;
+
+                       if (_contentPresenter != null)
+                               _contentPresenter.CharacterSpacing = CharacterSpacing;
+
+                       if(Content is TextBlock tb)
+                       {
+                               tb.CharacterSpacing = CharacterSpacing;
+                       }
+
+                       if (Content is StackPanel sp)
+                       {
+                               foreach (var item in sp.Children)
+                               {
+                                       if (item is TextBlock textBlock)
+                                       {
+                                               textBlock.CharacterSpacing = CharacterSpacing;
+                                       }
+                               }
+                       }
+
+               }
        }
 }
\ No newline at end of file
index 7dca835..3b9217f 100644 (file)
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using Windows.Foundation;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Automation.Peers;
 using Windows.UI.Xaml.Controls;
@@ -29,6 +30,8 @@ namespace Xamarin.Forms.Platform.UWP
                        if (span.IsSet(Span.TextDecorationsProperty))
                                run.TextDecorations = (Windows.UI.Text.TextDecorations)span.TextDecorations;
 
+                       run.CharacterSpacing = span.CharacterSpacing.ToEm();
+
                        return run;
                }
        }
@@ -140,6 +143,7 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateTextDecorations(Control);
                                UpdateColor(Control);
                                UpdateAlign(Control);
+                               UpdateCharacterSpacing(Control);
                                UpdateFont(Control);
                                UpdateLineBreakMode(Control);
                                UpdateMaxLines(Control);
@@ -164,6 +168,8 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateFont(Control);
                        else if (e.PropertyName == Label.TextDecorationsProperty.PropertyName)
                                UpdateTextDecorations(Control);
+                       else if (e.PropertyName == Label.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing(Control);
                        else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName)
                                UpdateLineBreakMode(Control);
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
@@ -302,6 +308,12 @@ namespace Xamarin.Forms.Platform.UWP
                        }
                }
 
+               void UpdateCharacterSpacing(TextBlock textBlock)
+               {
+                       textBlock.CharacterSpacing = Element.CharacterSpacing.ToEm();
+               }
+
+
                void DetermineTruncatedTextWrapping(TextBlock textBlock)
                {
                        if (Element.MaxLines > 1)
index 5be02cd..502cc81 100644 (file)
@@ -1,7 +1,12 @@
 using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.ComponentModel;
+using System.Linq;
 using System.Threading.Tasks;
 using Windows.UI.Core;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Media;
@@ -54,10 +59,10 @@ namespace Xamarin.Forms.Platform.UWP
                                        WireUpFormsVsm();
                                }
 
-                               Control.ItemsSource = ((LockableObservableListWrapper)Element.Items)._list;
-
+                               Control.ItemsSource = GetItems(Element.Items);
                                UpdateTitle();
                                UpdateSelectedIndex();
+                               UpdateCharacterSpacing();
                        }
 
                        base.OnElementChanged(e);
@@ -71,6 +76,8 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateSelectedIndex();
                        else if (e.PropertyName == Picker.TitleProperty.PropertyName || e.PropertyName == Picker.TitleColorProperty.PropertyName)
                                UpdateTitle();
+                       else if (e.PropertyName == Picker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Picker.TextColorProperty.PropertyName)
                                UpdateTextColor();
                        else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName || e.PropertyName == Picker.FontSizeProperty.PropertyName)
@@ -165,6 +172,40 @@ namespace Xamarin.Forms.Platform.UWP
                        });
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.CharacterSpacing = Element.CharacterSpacing.ToEm();
+
+                       if (Control.Header is TextBlock header)
+                       {
+                               header.CharacterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+
+                       if (Control.SelectedValue is TextBlock item)
+                       {
+                               item.CharacterSpacing = Element.CharacterSpacing.ToEm();
+                       }
+
+                       if(Control.ItemsSource is ObservableCollection<TextBlock> collection)
+                       {
+                               collection.ForEach(f=>f.CharacterSpacing = Control.CharacterSpacing);
+                       }
+               }
+
+
+               TextBlock ConvertStrongToTextBlock(string text)
+               {
+                       return new TextBlock{
+                               Text = text,
+                               CharacterSpacing = Control.CharacterSpacing
+                       };
+               }
+
+               ObservableCollection<TextBlock> GetItems(IList<string> items)
+               {
+                       return new ObservableCollection<TextBlock>(items.Select(ConvertStrongToTextBlock));
+               }
+
                void UpdateFont()
                {
                        if (Control == null)
@@ -214,7 +255,11 @@ namespace Xamarin.Forms.Platform.UWP
                        if (!Element.IsSet(Picker.TitleColorProperty))
                        {
                                Control.HeaderTemplate = null;
-                               Control.Header = Element.Title;
+                               Control.Header = new TextBlock
+                               {
+                                       Text = Element.Title ?? string.Empty,
+                                       CharacterSpacing = Element.CharacterSpacing.ToEm(),
+                               };
                        }
                        else
                        {
index 65f796a..875e606 100644 (file)
@@ -1,4 +1,5 @@
 using System.ComponentModel;
+using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Media;
@@ -39,6 +40,7 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdatePlaceholder();
                                UpdateCancelButtonColor();
                                UpdateAlignment();
+                               UpdateCharacterSpacing();
                                UpdateFont();
                                UpdateTextColor();
                                UpdatePlaceholderColor();
@@ -62,6 +64,8 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateAlignment();
                        else if (e.PropertyName == SearchBar.FontAttributesProperty.PropertyName)
                                UpdateFont();
+                       else if (e.PropertyName == SearchBar.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == SearchBar.FontFamilyProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == SearchBar.FontSizeProperty.PropertyName)
@@ -186,6 +190,11 @@ namespace Xamarin.Forms.Platform.UWP
                        _fontApplied = true;
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.CharacterSpacing = Element.CharacterSpacing.ToEm();
+               }
+
                void UpdatePlaceholder()
                {
                        Control.PlaceholderText = Element.Placeholder ?? string.Empty;
index 58d9c49..d717256 100644 (file)
@@ -3,6 +3,7 @@ using System.ComponentModel;
 using System.Linq;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
+using Windows.UI.Text;
 using Windows.UI.Xaml.Media;
 using Xamarin.Forms.Internals;
 
@@ -46,6 +47,7 @@ namespace Xamarin.Forms.Platform.UWP
                                }
 
                                UpdateTime();
+                               UpdateCharacterSpacing();
                                UpdateFlowDirection();
                        }
                }
@@ -86,6 +88,8 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateTextColor();
                        else if (e.PropertyName == TimePicker.FontAttributesProperty.PropertyName || e.PropertyName == TimePicker.FontFamilyProperty.PropertyName || e.PropertyName == TimePicker.FontSizeProperty.PropertyName)
                                UpdateFont();
+                       else if (e.PropertyName == TimePicker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
 
                        if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateFlowDirection();
@@ -147,6 +151,11 @@ namespace Xamarin.Forms.Platform.UWP
                        Control.Time = Element.Time;
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.CharacterSpacing = Element.CharacterSpacing.ToEm();
+               }
+
                void UpdateTextColor()
                {
                        Color color = Element.TextColor;
index 828f206..9f1823b 100644 (file)
@@ -1,3 +1,4 @@
+using Foundation;
 using UIKit;
 using Xamarin.Forms.Internals;
 
@@ -112,6 +113,51 @@ namespace Xamarin.Forms.Platform.iOS
                        }
                }
 
+               internal static NSMutableAttributedString AddCharacterSpacing(this NSMutableAttributedString attributedString, string text, double characterSpacing)
+               {
+                       if (attributedString == null || attributedString.Length == 0)
+                       {
+                               attributedString = text == null ? new NSMutableAttributedString() : new NSMutableAttributedString(text);
+                       }
+                       else
+                       {
+                               attributedString = new NSMutableAttributedString(attributedString);
+                       }
+
+                       AddKerningAdjustment(attributedString, text, characterSpacing);
+
+                       return attributedString;
+               }
+
+               internal static NSMutableAttributedString AddCharacterSpacing(this NSAttributedString attributedString, string text, double characterSpacing)
+               {
+                       NSMutableAttributedString mutableAttributedString;
+                       if (attributedString == null || attributedString.Length == 0)
+                       {
+                               mutableAttributedString = text == null ? new NSMutableAttributedString() : new NSMutableAttributedString(text);
+                       }
+                       else
+                       {
+                               mutableAttributedString = new NSMutableAttributedString(attributedString);
+                       }
+
+                       AddKerningAdjustment(mutableAttributedString, text, characterSpacing);
+
+                       return mutableAttributedString;
+               }
+
+               internal static void AddKerningAdjustment(NSMutableAttributedString mutableAttributedString, string text, double characterSpacing)
+               {
+                       if (!string.IsNullOrEmpty(text))
+                       {
+                               mutableAttributedString.AddAttribute
+                               (
+                                       UIStringAttributeKey.KerningAdjustment,
+                                       NSObject.FromObject(characterSpacing), new NSRange(0, text.Length - 1)
+                               );
+                       }
+               }
+
                internal static bool IsHorizontal(this Button.ButtonContentLayout layout) =>
                        layout.Position == Button.ButtonContentLayout.ImagePosition.Left ||
                        layout.Position == Button.ButtonContentLayout.ImagePosition.Right;
index 3ab36a9..920a45c 100644 (file)
@@ -92,6 +92,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                                UpdateFont();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
                                _buttonLayoutManager?.Update();
                        }
                }
@@ -108,7 +109,14 @@ namespace Xamarin.Forms.Platform.iOS
                        if (e.PropertyName == Button.TextColorProperty.PropertyName)
                                UpdateTextColor();
                        else if (e.PropertyName == Button.FontProperty.PropertyName)
+                       {
                                UpdateFont();
+                       }
+                       else if (e.PropertyName == Button.CharacterSpacingProperty.PropertyName)
+                       {
+                               UpdateCharacterSpacing();
+                       }
+
                }
 
                protected override void SetAccessibilityLabel()
@@ -151,6 +159,14 @@ namespace Xamarin.Forms.Platform.iOS
                        Control.TitleLabel.Font = Element.ToUIFont();
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       var attributedString = new NSMutableAttributedString(Element.Text ?? string.Empty).AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+                       Control.SetAttributedTitle(attributedString, UIControlState.Normal);
+                       Control.SetAttributedTitle(attributedString, UIControlState.Highlighted);
+                       Control.SetAttributedTitle(attributedString, UIControlState.Disabled);
+               }
+
                public void SetImage(UIImage image) => _buttonLayoutManager.SetImage(image);
 
                public UIImageView GetImage() => Control?.ImageView;
index 676ebbf..ed00e89 100644 (file)
@@ -89,6 +89,7 @@ namespace Xamarin.Forms.Platform.iOS
                        UpdateMaximumDate();
                        UpdateMinimumDate();
                        UpdateTextColor();
+                       UpdateCharacterSpacing();
                        UpdateFlowDirection();
                }
 
@@ -97,17 +98,25 @@ namespace Xamarin.Forms.Platform.iOS
                        base.OnElementPropertyChanged(sender, e);
 
                        if (e.PropertyName == DatePicker.DateProperty.PropertyName || e.PropertyName == DatePicker.FormatProperty.PropertyName)
+                       {
                                UpdateDateFromModel(true);
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName)
                                UpdateMinimumDate();
                        else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName)
                                UpdateMaximumDate();
+                       else if (e.PropertyName == DatePicker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
                                UpdateTextColor();
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateFlowDirection();
-                       else if (e.PropertyName == DatePicker.FontAttributesProperty.PropertyName || e.PropertyName == DatePicker.FontFamilyProperty.PropertyName || e.PropertyName == DatePicker.FontSizeProperty.PropertyName)
+                       else if (e.PropertyName == DatePicker.FontAttributesProperty.PropertyName ||
+                                e.PropertyName == DatePicker.FontFamilyProperty.PropertyName || e.PropertyName == DatePicker.FontSizeProperty.PropertyName)
+                       {
                                UpdateFont();
+                       }
                }
 
                void HandleValueChanged(object sender, EventArgs e)
@@ -143,6 +152,10 @@ namespace Xamarin.Forms.Platform.iOS
                        Control.Font = Element.ToUIFont();
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.AttributedText = Control.AttributedText.AddCharacterSpacing(Control.Text, Element.CharacterSpacing);
+               }
                void UpdateMaximumDate()
                {
                        _picker.MaximumDate = Element.MaximumDate.ToNSDate();
index 8011190..5e32a54 100644 (file)
@@ -63,7 +63,13 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        _placeholderLabel.Text = Element.Placeholder;
                }
-               
+
+               protected internal override void UpdateCharacterSpacing()
+               {
+                       TextView.AttributedText = TextView.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+                       _placeholderLabel.AttributedText = _placeholderLabel.AttributedText.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
+               }
+
                protected internal override void UpdatePlaceholderColor()
                {
                        Color placeholderColor = Element.PlaceholderColor;
@@ -99,6 +105,7 @@ namespace Xamarin.Forms.Platform.iOS
                        );
 
                        _placeholderLabel.TranslatesAutoresizingMaskIntoConstraints = false;
+                       _placeholderLabel.AttributedText = _placeholderLabel.AttributedText.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
 
                        Control.AddConstraints(hConstraints);
                        Control.AddConstraints(vConstraints);
@@ -174,6 +181,7 @@ namespace Xamarin.Forms.Platform.iOS
                        UpdatePlaceholderColor();
                        UpdateTextColor();
                        UpdateText();
+                       UpdateCharacterSpacing();
                        UpdateKeyboard();
                        UpdateEditable();
                        UpdateTextAlignment();
@@ -198,7 +206,10 @@ namespace Xamarin.Forms.Platform.iOS
                        base.OnElementPropertyChanged(sender, e);
 
                        if (e.PropertyName == Editor.TextProperty.PropertyName)
+                       {
                                UpdateText();
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName)
                                UpdateKeyboard();
                        else if (e.PropertyName == Xamarin.Forms.InputView.IsSpellCheckEnabledProperty.PropertyName)
@@ -215,12 +226,17 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateFont();
                        else if (e.PropertyName == Editor.FontSizeProperty.PropertyName)
                                UpdateFont();
+                       else if (e.PropertyName == Editor.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateTextAlignment();
                        else if (e.PropertyName == Xamarin.Forms.InputView.MaxLengthProperty.PropertyName)
                                UpdateMaxLength();
                        else if (e.PropertyName == Editor.PlaceholderProperty.PropertyName)
+                       {
                                UpdatePlaceholderText();
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == Editor.PlaceholderColorProperty.PropertyName)
                                UpdatePlaceholderColor();
                        else if (e.PropertyName == Editor.AutoSizeProperty.PropertyName)
@@ -306,7 +322,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                protected internal abstract void UpdatePlaceholderText();
                protected internal abstract void UpdatePlaceholderColor();
-
+               protected internal abstract void UpdateCharacterSpacing();
 
                void UpdateTextAlignment()
                {
index 3db72c9..e3378ec 100644 (file)
@@ -132,6 +132,7 @@ namespace Xamarin.Forms.Platform.iOS
                        UpdatePlaceholder();
                        UpdatePassword();
                        UpdateText();
+                       UpdateCharacterSpacing();
                        UpdateColor();
                        UpdateKeyboard();
                        UpdateAlignment();
@@ -153,9 +154,14 @@ namespace Xamarin.Forms.Platform.iOS
                        else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
                                UpdatePassword();
                        else if (e.PropertyName == Entry.TextProperty.PropertyName)
+                       {
                                UpdateText();
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
                                UpdateColor();
+                       else if (e.PropertyName == Entry.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName)
                                UpdateKeyboard();
                        else if (e.PropertyName == Xamarin.Forms.InputView.IsSpellCheckEnabledProperty.PropertyName)
@@ -332,6 +338,8 @@ namespace Xamarin.Forms.Platform.iOS
                                var color = targetColor.IsDefault ? _defaultPlaceholderColor : targetColor;
                                Control.AttributedPlaceholder = formatted.ToAttributed(Element, color);
                        }
+
+                       Control.AttributedPlaceholder = Control.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
                }
 
                void UpdateText()
@@ -341,6 +349,12 @@ namespace Xamarin.Forms.Platform.iOS
                                Control.Text = Element.Text;
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.AttributedText = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+                       Control.AttributedPlaceholder = Control.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
+               }
+
                void UpdateMaxLength()
                {
                        var currentControlText = Control.Text;
index 61ba295..ce024b4 100644 (file)
@@ -15,10 +15,10 @@ namespace Xamarin.Forms.Platform.MacOS
        public static class FormattedStringExtensions
        {
                public static NSAttributedString ToAttributed(this Span span, Font defaultFont, Color defaultForegroundColor)
-               {
+               { 
                        if (span == null)
                                return null;
-
+       
 #pragma warning disable 0618 //retaining legacy call to obsolete code
                        var font = span.Font != Font.Default ? span.Font : defaultFont;
 #pragma warning restore 0618
@@ -29,10 +29,11 @@ namespace Xamarin.Forms.Platform.MacOS
                                fgcolor = Color.Black; // as defined by apple docs              
 
 #if __MOBILE__
-                       return new NSAttributedString(span.Text, font == Font.Default ? null : font.ToUIFont(), fgcolor.ToUIColor(), span.BackgroundColor.ToUIColor());
+                       return new NSAttributedString(span.Text, font == Font.Default ? null : font.ToUIFont(), fgcolor.ToUIColor(), 
+                               span.BackgroundColor.ToUIColor(), kerning: (float)span.CharacterSpacing);
 #else
                        return new NSAttributedString(span.Text, font == Font.Default ? null : font.ToNSFont(), fgcolor.ToNSColor(),
-                               span.BackgroundColor.ToNSColor());
+                               span.BackgroundColor.ToNSColor(), kerningAdjustment: (float)span.CharacterSpacing);
 #endif
                }
 
@@ -127,10 +128,15 @@ namespace Xamarin.Forms.Platform.MacOS
                                hasUnderline = (textDecorations & TextDecorations.Underline) != 0;
                                hasStrikethrough = (textDecorations & TextDecorations.Strikethrough) != 0;
                        }
-
+#if __MOBILE__
                        var attrString = new NSAttributedString(text, targetFont, spanFgColor, spanBgColor,
                                underlineStyle: hasUnderline ? NSUnderlineStyle.Single : NSUnderlineStyle.None,
-                               strikethroughStyle: hasStrikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None, paragraphStyle: style);
+                               strikethroughStyle: hasStrikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None, paragraphStyle: style, kerning: (float)span.CharacterSpacing);
+#else
+                       var attrString = new NSAttributedString(text, targetFont, spanFgColor, spanBgColor,
+                               underlineStyle: hasUnderline ? NSUnderlineStyle.Single : NSUnderlineStyle.None,
+                               strikethroughStyle: hasStrikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None, paragraphStyle: style, kerningAdjustment: (float)span.CharacterSpacing);
+#endif
 
                        return attrString;
                }
index acf2312..d28c3eb 100644 (file)
@@ -172,6 +172,7 @@ namespace Xamarin.Forms.Platform.MacOS
                                UpdateTextColor();
                                UpdateFont();
                                UpdateMaxLines();
+                               UpdateCharacterSpacing();
                                UpdatePadding();
                        }
 
@@ -194,7 +195,10 @@ namespace Xamarin.Forms.Platform.MacOS
                        {
                                UpdateText();
                                UpdateTextDecorations();
+                               UpdateCharacterSpacing();
                        }
+                       else if (e.PropertyName == Label.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Label.TextDecorationsProperty.PropertyName)
                                UpdateTextDecorations();
                        else if (e.PropertyName == Label.FormattedTextProperty.PropertyName)
@@ -267,7 +271,8 @@ namespace Xamarin.Forms.Platform.MacOS
                                newAttributedText.AddAttribute(underlineStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range);
 
 #if __MOBILE__
-                       Control.AttributedText = newAttributedText;
+
+                       Control.AttributedText = newAttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
 #else
                        Control.AttributedStringValue = newAttributedText;
 #endif
@@ -364,6 +369,13 @@ namespace Xamarin.Forms.Platform.MacOS
 #endif
                }
 
+               void UpdateCharacterSpacing()
+               {
+#if __MOBILE__
+                       Control.AttributedText = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+#endif
+               }
+
                void UpdateText()
                {
                        _formatted = Element.FormattedText;
index 49a3678..4cd45fb 100644 (file)
@@ -71,6 +71,7 @@ namespace Xamarin.Forms.Platform.iOS
                                                        UpdatePickerSelectedIndex(0);
                                                UpdatePickerFromModel(s);
                                                entry.ResignFirstResponder();
+                                               UpdateCharacterSpacing();
                                        });
 
                                        toolbar.SetItems(new[] { spacer, doneButton }, false);
@@ -96,6 +97,7 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateFont();
                                UpdatePicker();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
 
                                ((INotifyCollectionChanged)e.NewElement.Items).CollectionChanged += RowsCollectionChanged;
                        }
@@ -107,13 +109,24 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        base.OnElementPropertyChanged(sender, e);
                        if (e.PropertyName == Picker.TitleProperty.PropertyName || e.PropertyName == Picker.TitleColorProperty.PropertyName)
+                       {
                                UpdatePicker();
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
+                       {
                                UpdatePicker();
+                               UpdateCharacterSpacing();
+                       }
+                       else if (e.PropertyName == Picker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == Picker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
                                UpdateTextColor();
-                       else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName || e.PropertyName == Picker.FontSizeProperty.PropertyName)
+                       else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName ||
+                                e.PropertyName == Picker.FontSizeProperty.PropertyName)
+                       {
                                UpdateFont();
+                       }
                }
 
                void OnEditing(object sender, EventArgs eventArgs)
@@ -144,9 +157,16 @@ namespace Xamarin.Forms.Platform.iOS
                void RowsCollectionChanged(object sender, EventArgs e)
                {
                        UpdatePicker();
+                       UpdateCharacterSpacing();
                }
 
-               protected internal virtual void UpdateFont()
+        protected void UpdateCharacterSpacing()
+        {
+               Control.AttributedText = Control.AttributedText.AddCharacterSpacing(Control.Text, Element.CharacterSpacing);
+                       Control.AttributedPlaceholder = Control.AttributedPlaceholder.AddCharacterSpacing(Element.Title, Element.CharacterSpacing);
+        }
+
+        protected internal virtual void UpdateFont()
                {
                        Control.Font = Element.ToUIFont();
                }
@@ -172,6 +192,8 @@ namespace Xamarin.Forms.Platform.iOS
                                var color = targetColor.IsDefault ? _defaultPlaceholderColor : targetColor;
                                Control.AttributedPlaceholder = formatted.ToAttributed(Element, color);
                        }
+
+                       Control.AttributedPlaceholder = Control.AttributedPlaceholder.AddCharacterSpacing(Element.Title, Element.CharacterSpacing);
                }
 
 
@@ -190,6 +212,7 @@ namespace Xamarin.Forms.Platform.iOS
                                return;
 
                        UpdatePickerSelectedIndex(selectedIndex);
+                       UpdateCharacterSpacing();
                }
 
                void UpdatePickerFromModel(PickerSource s)
index abf431d..127e9ce 100644 (file)
@@ -77,6 +77,7 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateCancelButton();
                                UpdateAlignment();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
                                UpdateMaxLength();
                                UpdateKeyboard();
                        }
@@ -98,14 +99,21 @@ namespace Xamarin.Forms.Platform.iOS
                        }
                        else if (e.PropertyName == SearchBar.TextColorProperty.PropertyName)
                                UpdateTextColor();
+                       else if (e.PropertyName == SearchBar.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
                        else if (e.PropertyName == SearchBar.TextProperty.PropertyName)
+                       {
                                UpdateText();
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == SearchBar.CancelButtonColorProperty.PropertyName)
                                UpdateCancelButton();
                        else if (e.PropertyName == SearchBar.FontAttributesProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == SearchBar.FontFamilyProperty.PropertyName)
+                       {
                                UpdateFont();
+                       }
                        else if (e.PropertyName == SearchBar.FontSizeProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName)
@@ -187,6 +195,15 @@ namespace Xamarin.Forms.Platform.iOS
                        UpdateOnTextChanged();
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       _textField = _textField ?? Control.FindDescendantView<UITextField>();
+                       if (_textField == null)
+                               return;
+                       _textField.AttributedText = _textField.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
+                       _textField.AttributedPlaceholder = _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
+               }
+
                void UpdateAlignment()
                {
                        _textField = _textField ?? Control.FindDescendantView<UITextField>();
@@ -262,11 +279,14 @@ namespace Xamarin.Forms.Platform.iOS
                                        ? targetColor : ColorExtensions.SeventyPercentGrey.ToColor();
 
                                _textField.AttributedPlaceholder = formatted.ToAttributed(Element, color);
+                               _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
+
                        }
                        else
                        {
                                _textField.AttributedPlaceholder = formatted.ToAttributed(Element, targetColor.IsDefault 
                                        ? ColorExtensions.SeventyPercentGrey.ToColor() : targetColor);
+                               _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
                        }
                }
 
index 6d85dba..f583de0 100644 (file)
@@ -97,6 +97,7 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateFont();
                                UpdateTime();
                                UpdateTextColor();
+                               UpdateCharacterSpacing();
                                UpdateFlowDirection();
                        }
 
@@ -108,10 +109,16 @@ namespace Xamarin.Forms.Platform.iOS
                        base.OnElementPropertyChanged(sender, e);
 
                        if (e.PropertyName == TimePicker.TimeProperty.PropertyName || e.PropertyName == TimePicker.FormatProperty.PropertyName)
+                       {
                                UpdateTime();
+                               UpdateCharacterSpacing();
+                       }
                        else if (e.PropertyName == TimePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
                                UpdateTextColor();
-                       else if (e.PropertyName == TimePicker.FontAttributesProperty.PropertyName || e.PropertyName == TimePicker.FontFamilyProperty.PropertyName || e.PropertyName == TimePicker.FontSizeProperty.PropertyName)
+                       else if (e.PropertyName == TimePicker.CharacterSpacingProperty.PropertyName)
+                               UpdateCharacterSpacing();
+                       else if (e.PropertyName == TimePicker.FontAttributesProperty.PropertyName ||
+                                e.PropertyName == TimePicker.FontFamilyProperty.PropertyName || e.PropertyName == TimePicker.FontSizeProperty.PropertyName)
                                UpdateFont();
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateFlowDirection();
@@ -155,6 +162,11 @@ namespace Xamarin.Forms.Platform.iOS
                        Control.Text = Control.Text;
                }
 
+               void UpdateCharacterSpacing()
+               {
+                       Control.AttributedText = Control.AttributedText.AddCharacterSpacing(Control.Text, Element.CharacterSpacing);
+               }
+
                void UpdateTime()
                {
                        _picker.Date = new DateTime(1, 1, 1).Add(Element.Time).ToNSDate();