Skip to content

Instantly share code, notes, and snippets.

@yuv4ik
Last active December 28, 2020 16:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yuv4ik/c7137c4ea89ededa99dfee51bfb1de4e to your computer and use it in GitHub Desktop.
Save yuv4ik/c7137c4ea89ededa99dfee51bfb1de4e to your computer and use it in GitHub Desktop.
Xamarin.Forms DatePicker with hours, minutes and seconds: TimeCountdownPicker
/*
Xaml example.
*/
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:YourNameSpace"
x:Class="YourNameSpace.ContentPage">
<local:TimeCountdownPicker SelectedTime="{Binding Countdown}" WidthRequest="85" />
</ContentPage>
/*
Special thanks:
@Mathieu who shared his solution on stackoVerflow.
http://stackoverflow.com/questions/35931470/timepicker-with-seconds/35954206#35954206
http://stackoverflow.com/users/659546/mathieu
Original solution was based on @XLabs, in this example the dependency is removed.
https://github.com/XLabs/Xamarin-Forms-Labs
*/
using System;
using Xamarin.Forms;
namespace YourNameSpace
{
public class TimeCountdownPicker : Picker
{
public static readonly BindableProperty SelectedTimeProperty =
BindableProperty.Create(nameof(SelectedTime), typeof(TimeSpan), typeof(TimeCountdownPicker), defaultValue: TimeSpan.Zero, defaultBindingMode: BindingMode.TwoWay, propertyChanged: OnSelectedTimePropertyPropertyChanged);
public TimeCountdownPicker()
{
// Add only one item, later will manipulate only it's value for performance
Items.Add("00:00:00");
SelectedIndex = 0;
SelectedTime = TimeSpan.Zero;
}
public TimeSpan SelectedTime
{
get { return (TimeSpan)GetValue(SelectedTimeProperty); }
set { SetValue(SelectedTimeProperty, value); }
}
private static void OnSelectedTimePropertyPropertyChanged(BindableObject bindable, object value, object newValue)
{
var picker = (TimeCountdownPicker)bindable;
// Update value
picker.Items[0] = newValue.ToString();
picker.SelectedIndex = 0;
}
}
}
/*
Special thanks:
@Mathieu who shared his solution on stackoVerflow.
http://stackoverflow.com/questions/35931470/timepicker-with-seconds/35954206#35954206
http://stackoverflow.com/users/659546/mathieu
Original solution was based on @XLabs, in this example the dependency is removed.
https://github.com/XLabs/Xamarin-Forms-Labs
*/
using System;
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using YourNameSpace;
using YourNameSpace.iOS;
[assembly: ExportRendererAttribute(typeof(TimeCountdownPicker), typeof(TimeCountdownPickerRenderer))]
namespace YourNameSpace.iOS
{
public class TimeCountdownPickerRenderer : PickerRenderer
{
public static double DisplayScale = UIScreen.MainScreen.Scale;
public static int DispalyHeight = (int)UIScreen.MainScreen.NativeBounds.Height;
public static int DisplayWidth = (int)UIScreen.MainScreen.NativeBounds.Width;
internal const int ComponentCount = 6;
private const int _labelSize = 30;
private TimeCountdownPicker timeCountdownPicker;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.BorderStyle = UITextBorderStyle.None;
timeCountdownPicker = e.NewElement as TimeCountdownPicker;
var customModelPickerView = new UIPickerView
{
Model = new TimeCountdownPickerView(timeCountdownPicker)
};
SelectPickerValue(customModelPickerView, timeCountdownPicker);
CreatePickerLabels(customModelPickerView);
Control.InputView = customModelPickerView;
}
}
private void SelectPickerValue(UIPickerView customModelPickerView, TimeCountdownPicker myTimePicker)
{
if (myTimePicker == null)
return;
customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Hours), 0, false);
customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Minutes), 2, false);
customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Seconds), 4, false);
}
private void CreatePickerLabels(UIPickerView customModelPickerView)
{
nfloat verticalPosition = (customModelPickerView.Frame.Size.Height / 2) - (_labelSize / 2);
nfloat componentWidth = new nfloat(DisplayWidth / ComponentCount /DisplayScale);
var hoursLabel = new UILabel(new CGRect(componentWidth, verticalPosition, _labelSize, _labelSize));
hoursLabel.Text = "h";
var minutesLabel = new UILabel(new CGRect((componentWidth * 3) + (componentWidth / 2), verticalPosition, _labelSize, _labelSize));
minutesLabel.Text = "m";
var secondsLabel = new UILabel(new CGRect((componentWidth * 5) + (componentWidth / 2), verticalPosition, _labelSize, _labelSize));
secondsLabel.Text = "s";
customModelPickerView.AddSubview(hoursLabel);
customModelPickerView.AddSubview(minutesLabel);
customModelPickerView.AddSubview(secondsLabel);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null)
return;
if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
{
var customModelPickerView = (UIPickerView)Control.InputView;
SelectPickerValue(customModelPickerView, timeCountdownPicker);
}
}
public class TimeCountdownPickerView : UIPickerViewModel
{
private TimeCountdownPicker timeCountdownPicker { get; }
public TimeCountdownPickerView(TimeCountdownPicker picker)
{
timeCountdownPicker = picker;
}
public override nint GetComponentCount(UIPickerView pickerView)
{
return new nint(ComponentCount);
}
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
if (component == 0)
{
// Hours
return new nint(24);
}
if (component % 2 != 0)
{
// Odd components are labels for hrs, mins and secs
return new nint(1);
}
// Minutes & seconds
return new nint(60);
}
public override string GetTitle(UIPickerView pickerView, nint row, nint component)
{
if (component == 0)
{
return row.ToString();
}
else if (component == 1)
{
return null;
}
else if (component == 3)
{
return null;
}
else if (component == 5)
{
return null;
}
return row.ToString("#0");
}
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
var selectedHours = pickerView.SelectedRowInComponent(0);
var selectedMinutes = pickerView.SelectedRowInComponent(2);
var selectedSeconds = pickerView.SelectedRowInComponent(4);
var time = new TimeSpan((int)selectedHours, (int)selectedMinutes, (int)selectedSeconds);
timeCountdownPicker.SelectedTime = time;
}
public override nfloat GetComponentWidth(UIPickerView pickerView, nint component)
{
var screenWidth = DisplayWidth;
var componentWidth = screenWidth /
ComponentCount /
DisplayScale;
return new nfloat(componentWidth);
}
}
}
}
@yuv4ik
Copy link
Author

yuv4ik commented Apr 5, 2018

@cullenjohnson I didn't get to test on smaller screens. I guess you could tweak it in CreatePickerLabels method.
Thanks for letting me now.

@kshumon2000
Copy link

Thanks so much @yuv4ik!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment