Forum Xamarin.Forms

How trigger the DateSelected event of DatePicker only if the user tap on "Done" on iOS?

voidstreamvoidstream FRMember ✭✭✭
edited December 2017 in Xamarin.Forms

Hello,

How trigger the DateSelected event of DatePicker only if the user tap on "Done" on iOS?

Currently the event is triggered if you change the year, month or date

Best regards.

Best Answer

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Instead of worrying about the event, if you bind to a property in your VM, I'm pretty sure the property isn't updated until the [DONE] happens.

  • ShantimohanElchuriShantimohanElchuri USMember ✭✭✭✭✭

    @voidstream said:
    Hello,

    How trigger the DateSelected event of DatePicker only if the user tap on "Done" on iOS?

    Currently the event is triggered if you change the year, month or date

    Best regards.

    In iOS, the Done button only dismisses the view. The date in the whatever you bind to will keep changing dynamically as you roll the date wheels. Unfortunately, you can't cancel the date set. I don't know how iOS native developers cope with it.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭
    edited December 2017

    @ShantimohanElchuri
    Oh man - that sucks.
    Can you keep an eye on the picker being visible and only proceed after it has gone up, then down?

  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited December 2017

    We ended up writing new renderers for the DatePicker and TimePicker which add a Cancel button to the toolbar to close the picker without updating the value, and only update the value in the Done button's handler.

    I made an Evolution forum post about this back in July but never got any response on it.

  • voidstreamvoidstream FRMember ✭✭✭
    edited December 2017

    @JoeManke said:
    We ended up writing new renderers for the DatePicker and TimePicker which add a Cancel button to the toolbar to close the picker without updating the value, and only update the value in the Done button's handler.

    I made an Evolution forum post about this back in July but never got any response on it.

    Great idea! How can i implement your code?

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    Start with the existing renderer source code.

    In the OnElementChanged method, replace this block:

    _picker.ValueChanged += HandleValueChanged;
    
    var width = UIScreen.MainScreen.Bounds.Width;
    var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true };
    var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
    var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) => entry.ResignFirstResponder());
    
    toolbar.SetItems(new[] { spacer, doneButton }, false);
    

    with this:

    var width = (float)UIScreen.MainScreen.Bounds.Width;
    var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true };
    var cancel = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, OnCanceled);
    var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
    var done = new UIBarButtonItem(UIBarButtonSystemItem.Done, OnDone);
    
    toolbar.SetItems(new[] { cancel, spacer, done }, false);
    

    Remove the HandleValueChanged method and replace it with these two methods:

    private void OnCanceled(object sender, EventArgs e)
    {
        Control.ResignFirstResponder();
    }
    
    private void OnDone(object sender, EventArgs e)
    {
        ElementController.SetValueFromRenderer(DatePicker.DateProperty, _picker.Date.ToDateTime().Date);
        Control.ResignFirstResponder();
    }
    
  • Matthew.4307Matthew.4307 USMember ✭✭✭

    Acr.UserDialogs date prompt only returns the value when the user accepts. We use this in combination with a textbox to display the required value, tapping on the textbox fires off to Acr to show the prompt. The reason this came about was due to the awfulness of the Windows 8.1 date picker control and it had a useful side-effect on iOS :smile:

  • voidstreamvoidstream FRMember ✭✭✭
    edited January 2018

    @JoeManke

    Hello, thanks for you answer!

    I have followed your instructions for update the DatePickerRenderer.cs but i have some issues.
    Summary of issues:

    • UseLegacyColorManagement() not exists
    • FlowDirectionProperty not exist for Xamarin.Forms.VisualElement and for Xamarin.Forms.PlatformConfiguration.iOSSpecific.VisualElement
    • FontAttributesProperty(), FontFamilyProperty(), FontSizeProperty() not exist
    • UpdateTextAlignment() not exists
    • ToUIFont() not exists

    Below the complete code and commented (in case of problem).

    using System.ComponentModel;
    using System;
    
    using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
    using Xamarin.Forms.Platform.iOS;
    using Xamarin.Forms;
    
    using UIKit;
    
    using RectangleF = CoreGraphics.CGRect;
    
    namespace MySolutionName.iOS.Renderers
    {
        internal class NoCaretField : UITextField
        {
            public NoCaretField() : base(new RectangleF())
            {
            }
    
            public override RectangleF GetCaretRectForPosition(UITextPosition position)
            {
                return new RectangleF();
            }
        }
    
        public class DatePickerRenderer : ViewRenderer<DatePicker, UITextField>
        {
            // Fields
            private UIDatePicker _picker;
            private UIColor _defaultTextColor;
            private bool _disposed;
            private bool _useLegacyColorManagement;
    
            // Properties
            private IElementController ElementController => Element as IElementController;
    
            // Methods (Events)
            protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
            {
                base.OnElementChanged(e);
    
                if (e.NewElement == null)
                    return;
    
                if (Control == null)
                {
                    var entry = new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
    
                    entry.EditingDidBegin += OnStarted;
                    entry.EditingDidEnd += OnEnded;
    
                    var width = (float)UIScreen.MainScreen.Bounds.Width;
                    var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true };
                    var cancel = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, OnCanceled);
                    var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
                    var done = new UIBarButtonItem(UIBarButtonSystemItem.Done, OnDone);
    
                    toolbar.SetItems(new[] { cancel, spacer, done }, false);
    
                    entry.InputView = _picker;
                    entry.InputAccessoryView = toolbar;
    
                    _defaultTextColor = entry.TextColor;
    
                    _useLegacyColorManagement = e.NewElement.UseLegacyColorManagement(); // UseLegacyColorManagement() not exists
    
                    SetNativeControl(entry);
                }
    
                UpdateDateFromModel(false);
                UpdateFont();
                UpdateMaximumDate();
                UpdateMinimumDate();
                UpdateTextColor();
                UpdateFlowDirection();
            }
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                base.OnElementPropertyChanged(sender, e);
    
                if (e.PropertyName == DatePicker.DateProperty.PropertyName || e.PropertyName == DatePicker.FormatProperty.PropertyName)
                    UpdateDateFromModel(true);
                else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName)
                    UpdateMinimumDate();
                else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName)
                    UpdateMaximumDate();
                else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName || e.PropertyName == Xamarin.Forms.VisualElement.IsEnabledProperty.PropertyName) // I force the reference to Xamarin.Forms, it's ok?
                    UpdateTextColor();
                else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)  // FlowDirectionProperty not exist for Xamarin.Forms.VisualElement and for Xamarin.Forms.PlatformConfiguration.iOSSpecific.VisualElement
                    UpdateFlowDirection();
                else if (e.PropertyName == DatePicker.FontAttributesProperty.PropertyName || e.PropertyName == DatePicker.FontFamilyProperty.PropertyName || e.PropertyName == DatePicker.FontSizeProperty.PropertyName) // FontAttributesProperty(), FontFamilyProperty(), FontSizeProperty() not exist
                    UpdateFont();
            }
    
            private void OnEnded(object sender, EventArgs eventArgs)
            {
                ElementController.SetValueFromRenderer(Xamarin.Forms.VisualElement.IsFocusedPropertyKey, false); // I force the reference to Xamarin.Forms, it's ok?
            }
            private void OnStarted(object sender, EventArgs eventArgs)
            {
                ElementController.SetValueFromRenderer(Xamarin.Forms.VisualElement.IsFocusedPropertyKey, true); // I force the reference to Xamarin.Forms, it's ok?
            }
            private void OnCanceled(object sender, EventArgs e)
            {
                Control.ResignFirstResponder();
            }
            private void OnDone(object sender, EventArgs e)
            {
                ElementController.SetValueFromRenderer(DatePicker.DateProperty, _picker.Date.ToDateTime().Date);
                Control.ResignFirstResponder();
            }
    
            // Methods (Update)
            private void UpdateDateFromModel(bool animate)
            {
                if (_picker.Date.ToDateTime().Date != Element.Date.Date)
                    _picker.SetDate(Element.Date.ToNSDate(), animate);
    
                Control.Text = Element.Date.ToString(Element.Format);
            }
            private void UpdateFlowDirection()
            {
                (Control as UITextField).UpdateTextAlignment(Element); // UpdateTextAlignment() not exists
            }
            private void UpdateFont()
            {
                Control.Font = Element.ToUIFont(); // ToUIFont() not exists
            }
            private void UpdateMaximumDate()
            {
                _picker.MaximumDate = Element.MaximumDate.ToNSDate();
            }
            private void UpdateMinimumDate()
            {
                _picker.MinimumDate = Element.MinimumDate.ToNSDate();
            }
            private void UpdateTextColor()
            {
                var textColor = Element.TextColor;
    
                if (textColor.IsDefault || (!Element.IsEnabled && _useLegacyColorManagement))
                    Control.TextColor = _defaultTextColor;
                else
                    Control.TextColor = textColor.ToUIColor();
    
                // HACK This forces the color to update; there's probably a more elegant way to make this happen
                Control.Text = Control.Text;
            }
    
            protected override void Dispose(bool disposing)
            {
                if (_disposed)
                    return;
    
                _disposed = true;
    
                if (disposing)
                {
                    _defaultTextColor = null;
    
                    if (_picker != null)
                    {
                        _picker.RemoveFromSuperview();
                        _picker.Dispose();
                        _picker = null;
                    }
    
                    if (Control != null)
                    {
                        Control.EditingDidBegin -= OnStarted;
                        Control.EditingDidEnd -= OnEnded;
                    }
                }
    
                base.Dispose(disposing);
            }
        }
    }
    

    Any ideas?

  • voidstreamvoidstream FRMember ✭✭✭

    @Matthew.4307 said:
    Acr.UserDialogs date prompt only returns the value when the user accepts. We use this in combination with a textbox to display the required value, tapping on the textbox fires off to Acr to show the prompt. The reason this came about was due to the awfulness of the Windows 8.1 date picker control and it had a useful side-effect on iOS :smile:

    Thanks for this information :smile:

  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited January 2018

    @voidstream said:
    @JoeManke

    Hello, thanks for you answer!

    I have followed your instructions for update the DatePickerRenderer.cs but i have some issues.
    Summary of issues:

    • UseLegacyColorManagement() not exists
    • FlowDirectionProperty not exist for Xamarin.Forms.VisualElement and for Xamarin.Forms.PlatformConfiguration.iOSSpecific.VisualElement
    • FontAttributesProperty(), FontFamilyProperty(), FontSizeProperty() not exist
    • UpdateTextAlignment() not exists
    • ToUIFont() not exists
      Any ideas?

    Looks like they've made some changes on the master branch. I would jump over to the tag matching whatever version of Forms you're currently on. Here's the link for the latest hotfix of 2.5.0.

  • NickKovalskyNickKovalsky USMember ✭✭✭

    Okay just process the Unfocused event on iOS safe effect as Done clicked

  • apisanoapisano Member

    Works like a charm. Very simple solution. Thanks.

Sign In or Register to comment.