Skip to content

Commit 106d4a9

Browse files
CopilotPureWeen
andcommitted
Implement fresh solution for iOS DatePicker null date handling with new nullable API
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
1 parent 736aa95 commit 106d4a9

File tree

5 files changed

+98
-162
lines changed

5 files changed

+98
-162
lines changed

src/Core/src/Handlers/DatePicker/DatePickerHandler.MacCatalyst.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ protected override void ConnectHandler(UIDatePicker platformView)
2525
{
2626
platformView.Date = dt.ToNSDate();
2727
}
28+
else
29+
{
30+
// When Date is null, use today's date for the picker display
31+
platformView.Date = DateTime.Today.ToNSDate();
32+
}
2833

2934
base.ConnectHandler(platformView);
3035
}

src/Core/src/Handlers/DatePicker/DatePickerHandler.iOS.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ protected override void ConnectHandler(MauiDatePicker platformView)
2525
var date = VirtualView?.Date;
2626
if (date is not null && date is DateTime dt)
2727
{
28-
// If date is equal to MinimumDate (could be default/null value), use Today's date for the picker
29-
var pickerDate = dt == VirtualView.MinimumDate ? DateTime.Today : dt;
30-
picker.Date = pickerDate.ToNSDate();
28+
picker.Date = dt.ToNSDate();
29+
}
30+
else
31+
{
32+
// When Date is null, use today's date for the picker display
33+
picker.Date = DateTime.Today.ToNSDate();
3134
}
3235
}
3336

src/Core/src/Platform/iOS/DatePickerExtensions.cs

Lines changed: 13 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -38,89 +38,7 @@ public static void UpdateTextColor(this MauiDatePicker platformDatePicker, IDate
3838
{
3939
if (defaultTextColor is not null)
4040
{
41-
if (defaultTextColor != null)
42-
{
43-
platformDatePicker.TextColor = defaultTextColor;
44-
}
45-
}
46-
else
47-
{
48-
platformDatePicker.TextColor = textColor.ToPlatform();
49-
}
50-
51-
}
52-
53-
public static void UpdateDate(this UIDatePicker picker, IDatePicker datePicker)
54-
{
55-
if (picker != null)
56-
{
57-
// If date is equal to MinimumDate (could be default/null value), use Today's date for the picker
58-
var date = datePicker.Date == datePicker.MinimumDate ? DateTime.Today : datePicker.Date;
59-
if (picker.Date.ToDateTime().Date != date.Date)
60-
picker.SetDate(date.ToNSDate(), false);
61-
}
62-
}
63-
64-
public static void UpdateDate(this MauiDatePicker platformDatePicker, IDatePicker datePicker, UIDatePicker? picker)
65-
{
66-
if (picker != null)
67-
{
68-
// If date is equal to MinimumDate (could be default/null value), use Today's date for the picker
69-
var date = datePicker.Date == datePicker.MinimumDate ? DateTime.Today : datePicker.Date;
70-
if (picker.Date.ToDateTime().Date != date.Date)
71-
picker.SetDate(date.ToNSDate(), false);
72-
}
73-
74-
string format = datePicker.Format ?? string.Empty;
75-
76-
// Can't use VirtualView.Format because it won't display the correct format if the region and language are set differently
77-
if (picker != null && (string.IsNullOrWhiteSpace(format) || format.Equals("d", StringComparison.OrdinalIgnoreCase)))
78-
{
79-
NSDateFormatter dateFormatter = new NSDateFormatter
80-
{
81-
TimeZone = NSTimeZone.FromGMT(0)
82-
};
83-
84-
if (format.Equals("D", StringComparison.Ordinal) == true)
85-
{
86-
dateFormatter.DateStyle = NSDateFormatterStyle.Long;
87-
var strDate = dateFormatter.StringFor(picker.Date);
88-
platformDatePicker.Text = strDate;
89-
}
90-
else
91-
{
92-
dateFormatter.DateStyle = NSDateFormatterStyle.Short;
93-
var strDate = dateFormatter.StringFor(picker.Date);
94-
platformDatePicker.Text = strDate;
95-
}
96-
}
97-
else if (format.Contains('/', StringComparison.Ordinal))
98-
{
99-
platformDatePicker.Text = datePicker.Date.ToString(format, CultureInfo.InvariantCulture);
100-
}
101-
else
102-
{
103-
platformDatePicker.Text = datePicker.Date.ToString(format);
104-
}
105-
106-
platformDatePicker.UpdateCharacterSpacing(datePicker);
107-
}
108-
109-
public static void UpdateMinimumDate(this MauiDatePicker platformDatePicker, IDatePicker datePicker)
110-
{
111-
platformDatePicker.UpdateMinimumDate(datePicker, null);
112-
}
113-
114-
public static void UpdateMinimumDate(this MauiDatePicker platformDatePicker, IDatePicker datePicker, UIDatePicker? picker)
115-
{
116-
picker?.UpdateMinimumDate(datePicker);
117-
}
118-
119-
public static void UpdateMinimumDate(this UIDatePicker platformDatePicker, IDatePicker datePicker)
120-
{
121-
if (platformDatePicker != null)
122-
{
123-
platformDatePicker.MinimumDate = datePicker.MinimumDate.ToNSDate();
41+
platformDatePicker.TextColor = defaultTextColor;
12442
}
12543
}
12644
else
@@ -134,17 +52,25 @@ public static void UpdateMinimumDate(this UIDatePicker platformDatePicker, IDate
13452

13553
public static void UpdateDate(this UIDatePicker picker, IDatePicker datePicker)
13654
{
137-
if (picker is not null && picker.Date.ToDateTime() != datePicker.Date)
55+
if (picker is not null)
13856
{
139-
picker.SetDate(datePicker.Date?.ToNSDate() ?? NSDate.DistantPast, false);
57+
var targetDate = datePicker.Date ?? DateTime.Today;
58+
if (picker.Date.ToDateTime() != targetDate)
59+
{
60+
picker.SetDate(targetDate.ToNSDate(), false);
61+
}
14062
}
14163
}
14264

14365
public static void UpdateDate(this MauiDatePicker platformDatePicker, IDatePicker datePicker, UIDatePicker? picker)
14466
{
145-
if (picker is not null && picker.Date != NSDate.DistantPast && picker.Date.ToDateTime() != datePicker.Date)
67+
if (picker is not null)
14668
{
147-
picker.SetDate(datePicker.Date?.ToNSDate() ?? NSDate.DistantPast, false);
69+
var targetDate = datePicker.Date ?? DateTime.Today;
70+
if (picker.Date != NSDate.DistantPast && picker.Date.ToDateTime() != targetDate)
71+
{
72+
picker.SetDate(targetDate.ToNSDate(), false);
73+
}
14874
}
14975

15076
string format = datePicker.Format ?? string.Empty;

src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.iOS.cs

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,55 @@ public async Task CharacterSpacingInitializesCorrectly()
8585
Assert.Equal(xplatCharacterSpacing, values.PlatformViewValue);
8686
}
8787

88+
[Fact(DisplayName = "Null Date Uses Today's Date in Picker")]
89+
public async Task NullDateUsesTodaysDateInPicker()
90+
{
91+
var datePicker = new DatePickerStub()
92+
{
93+
Date = null
94+
};
95+
96+
var values = await GetValueAsync(datePicker, (handler) =>
97+
{
98+
return new
99+
{
100+
ViewValue = datePicker.Date,
101+
PlatformViewValue = GetNativePickerDate(handler)
102+
};
103+
});
104+
105+
Assert.Null(values.ViewValue);
106+
Assert.Equal(DateTime.Today.Date, values.PlatformViewValue.Date);
107+
}
108+
109+
[Fact(DisplayName = "Null Date Updates To Today's Date When Picker Updates")]
110+
public async Task NullDateUpdatesToTodaysDateWhenPickerUpdates()
111+
{
112+
var datePicker = new DatePickerStub()
113+
{
114+
Date = null
115+
};
116+
117+
await InvokeOnMainThreadAsync(async () =>
118+
{
119+
var handler = CreateHandler(datePicker);
120+
var platformView = handler.PlatformView;
121+
122+
// Initially null date should show today's date in picker
123+
var picker = handler.DatePickerDialog;
124+
Assert.NotNull(picker);
125+
Assert.Equal(DateTime.Today.Date, picker.Date.ToDateTime().Date);
126+
127+
// Update to a specific date
128+
var testDate = new DateTime(2023, 5, 15);
129+
datePicker.Date = testDate;
130+
131+
// Verify the picker updates to the specific date
132+
await Task.Delay(100); // Allow update to process
133+
Assert.Equal(testDate.Date, picker.Date.ToDateTime().Date);
134+
});
135+
}
136+
88137
MauiDatePicker GetNativeDatePicker(DatePickerHandler datePickerHandler) =>
89138
datePickerHandler.PlatformView;
90139

@@ -115,6 +164,12 @@ DateTime GetNativeMaximumDate(DatePickerHandler datePickerHandler)
115164
return maxDate.ToDateTime();
116165
}
117166

167+
DateTime GetNativePickerDate(DatePickerHandler datePickerHandler)
168+
{
169+
var dialog = datePickerHandler.DatePickerDialog;
170+
return dialog.Date.ToDateTime();
171+
}
172+
118173
double GetNativeCharacterSpacing(DatePickerHandler datePickerHandler)
119174
{
120175
var mauiDatePicker = GetNativeDatePicker(datePickerHandler);
@@ -123,57 +178,6 @@ double GetNativeCharacterSpacing(DatePickerHandler datePickerHandler)
123178

124179
double GetNativeUnscaledFontSize(DatePickerHandler datePickerHandler) =>
125180
GetNativeDatePicker(datePickerHandler).Font.PointSize;
126-
127-
[Fact(DisplayName = "Date Picker Dialog Defaults to Today When Date is MinimumDate")]
128-
public async Task DatePickerDialogDefaultsToTodayWhenDateIsMinimumDate()
129-
{
130-
var datePicker = new DatePickerStub()
131-
{
132-
Date = new DateTime(1900, 1, 1) // Set to MinimumDate value
133-
};
134-
135-
var values = await GetValueAsync(datePicker, (handler) =>
136-
{
137-
var dialog = handler.DatePickerDialog;
138-
return new
139-
{
140-
VirtualViewDate = datePicker.Date,
141-
DialogDate = dialog?.Date.ToDateTime()
142-
};
143-
});
144-
145-
// VirtualView should have MinimumDate as set
146-
Assert.Equal(new DateTime(1900, 1, 1), values.VirtualViewDate);
147-
148-
// Dialog should default to Today's date when Date is MinimumDate
149-
Assert.Equal(DateTime.Today, values.DialogDate?.Date);
150-
}
151-
152-
[Fact(DisplayName = "Date Picker Dialog Uses Specified Date When Not MinimumDate")]
153-
public async Task DatePickerDialogUsesSpecifiedDateWhenNotMinimumDate()
154-
{
155-
var specificDate = new DateTime(2023, 6, 15);
156-
var datePicker = new DatePickerStub()
157-
{
158-
Date = specificDate
159-
};
160-
161-
var values = await GetValueAsync(datePicker, (handler) =>
162-
{
163-
var dialog = handler.DatePickerDialog;
164-
return new
165-
{
166-
VirtualViewDate = datePicker.Date,
167-
DialogDate = dialog?.Date.ToDateTime()
168-
};
169-
});
170-
171-
// VirtualView should have the specified date
172-
Assert.Equal(specificDate, values.VirtualViewDate);
173-
174-
// Dialog should also have the specified date
175-
Assert.Equal(specificDate, values.DialogDate?.Date);
176-
}
177181
}
178182
}
179183
#endif

src/Core/tests/UnitTests/Views/DatePickerTests.cs

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,37 @@ public void TestDatePickerFormat(string dateFormat)
2525
}
2626

2727
[Fact]
28-
public void TestDatePickerDefaultValue()
28+
public void TestNullDatePreservesNullValue()
2929
{
30-
DatePicker datePicker = new DatePicker();
30+
var datePicker = new DatePicker
31+
{
32+
Date = null
33+
};
3134

32-
// The default value should be Today's date due to the defaultValueCreator
33-
Assert.Equal(DateTime.Today, datePicker.Date);
35+
Assert.Null(datePicker.Date);
3436
}
3537

3638
[Fact]
37-
public void TestDatePickerMinValueHandling()
39+
public void TestNullDateDefaultsToToday()
3840
{
39-
DatePicker datePicker = new DatePicker();
41+
var datePicker = new DatePicker();
4042

41-
// Set date to MinValue explicitly - this should get coerced to MinimumDate
42-
datePicker.Date = DateTime.MinValue;
43-
44-
// The DatePicker should have MinimumDate as its value due to coercion
45-
Assert.Equal(new DateTime(1900, 1, 1), datePicker.Date);
43+
// Default value should be today
44+
Assert.Equal(DateTime.Today, datePicker.Date);
4645
}
4746

4847
[Fact]
49-
public void TestDatePickerWithCustomMinimumDate()
48+
public void TestNullDateCanBeSetAndRetrieved()
5049
{
51-
DatePicker datePicker = new DatePicker();
52-
53-
// Set a custom minimum date
54-
datePicker.MinimumDate = new DateTime(2020, 1, 1);
55-
56-
// Set date to MinValue - this should get coerced to the new MinimumDate
57-
datePicker.Date = DateTime.MinValue;
50+
var datePicker = new DatePicker
51+
{
52+
Date = DateTime.Today
53+
};
5854

59-
// The DatePicker should have the custom MinimumDate as its value
60-
Assert.Equal(new DateTime(2020, 1, 1), datePicker.Date);
55+
// Change to null
56+
datePicker.Date = null;
57+
58+
Assert.Null(datePicker.Date);
6159
}
6260
}
6361
}

0 commit comments

Comments
 (0)