Following on from my last post about iOS 13, I’ve since had the need to update various apps that have a segmented control. I’ve been using Alexander Reyes cool control Segmented Control Plugin for Xamarin.Forms (I believe there are a few others out there as well) to get keep everything nicely in XAML and working on Android as well, that doesn’t have the same sort of control that iOS does natively.
This has been working extremely nicely for years, until I started targeting iOS 13 and I got the following on all apps:
While the unselected options are legible, the selected one is just a white bar.
Thankfully, with a little bit of work in a custom renderer this can be quite simply solved to make your segmented controls a prettier in iOS 13 with Xamarin.Forms.
Firstly, using a third party library makes things a little trickier with custom renderers. This is very simply solved however by just creating a dumb control that inherits the library one.
Add the library to your projects
Then create a control that inherits from it
C#
1 2 3 4 5 6 7 8 9 10 |
using System; namespace PrettierSegmentControlsInIos13.Controls { public class CustomSegmentedControl : SegmentedControl.FormsPlugin.Abstractions.SegmentedControl { public CustomSegmentedControl() { } } } |
From here, we need to set the new iOS 13 properties to style things in the customer renderer to whatever colour palette the app has.
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
using System; using PrettierSegmentControlsInIos13.Controls; using PrettierSegmentControlsInIos13.iOS.Renderers; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(CustomSegmentedControl), typeof(CustomSegmentedControlRenderer))] namespace PrettierSegmentControlsInIos13.iOS.Renderers { public class CustomSegmentedControlRenderer : SegmentedControl.FormsPlugin.iOS.SegmentedControlRenderer { protected override void OnElementChanged(ElementChangedEventArgs e) { try { if (e.OldElement != null) { return; } base.OnElementChanged(e); if (Control != null) { if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0)) { Control.SelectedSegmentTintColor = Element.TintColor.ToUIColor(); Control.BackgroundColor = Element.BackgroundColor.ToUIColor(); Control.SetTitleTextAttributes(new UITextAttributes { TextColor = Element.SelectedTextColor.ToUIColor() }, UIControlState.Selected); // Note: In a real example this would likely be a global value and not the SelectedTextColor but for the purposes of this example we're just setting both to the same Control.SetTitleTextAttributes(new UITextAttributes { TextColor = Element.SelectedTextColor.ToUIColor() }, UIControlState.Normal); } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (Control == null || Element == null) return; // Leave everything as defaults for pre iOS 13 if (!UIDevice.CurrentDevice.CheckSystemVersion(13, 0)) { return; } switch (e.PropertyName) { case nameof(CustomSegmentedControl.TintColor): SetTintColor(); break; case nameof(CustomSegmentedControl.BackgroundColor): SetBackgroundColor(); break; case nameof(CustomSegmentedControl.SelectedTextColor): SetTextColor(); break; } } private void SetTintColor() { Control.SelectedSegmentTintColor = Element.TintColor.ToUIColor(); } private void SetBackgroundColor() { Control.BackgroundColor = Element.BackgroundColor.ToUIColor(); } private void SetTextColor() { Control.SetTitleTextAttributes(new UITextAttributes { TextColor = Element.SelectedTextColor.ToUIColor() }, UIControlState.Selected); // Note: In a real example this would likely be a global value and not the SelectedTextColor but for the purposes of this example we're just setting both to the same Control.SetTitleTextAttributes(new UITextAttributes { TextColor = Element.SelectedTextColor.ToUIColor() }, UIControlState.Normal); } } } |
Then it’s as simple as binding the properties (or just setting globally in the renderer from styles as I find is actually more common) on the control and we have a much nicer looking segmented control.
XAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<ContentPage ... xmlns:controls="clr-namespace:PrettierSegmentControlsInIos13.Controls" xmlns:segmented="clr-namespace:SegmentedControl.FormsPlugin.Abstractions;assembly=SegmentedControl.FormsPlugin.Abstractions" ... <controls:CustomSegmentedControl TintColor="{Binding SegmentTintColor}" BackgroundColor="{Binding SegmentBackgroundColor}" SelectedTextColor="{Binding SegmentTextColor}" Margin="0"> <segmented:SegmentedControl.Children> <segmented:SegmentedControlOption Text="Option 1" /> <segmented:SegmentedControlOption Text="Option 2" /> <segmented:SegmentedControlOption Text="Option 3" /> </segmented:SegmentedControl.Children> </controls:CustomSegmentedControl> ... |
And that’s it. Without too much fuss things are styled nicely and we can on with the job at hand with segmented controls looking nice again, if not nicer with the new animated effects.
As usual, if you want the entire working example, please have a look here on GitHub.
Leave A Comment