diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/IndicatorViewCircleShape.png new file mode 100644 index 000000000000..a6d1f0f81dab Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/IndicatorViewCircleShape.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/IndicatorViewSquareShape.png new file mode 100644 index 000000000000..f02e8006266e Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/IndicatorViewSquareShape.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31065.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31065.cs new file mode 100644 index 000000000000..37e3442e1ba3 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31065.cs @@ -0,0 +1,54 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 31065, "IndicatorView square shape does not update on load or dynamically", PlatformAffected.iOS | PlatformAffected.macOS)] +public class Issue31065 : ContentPage +{ + IndicatorView _indicator; + public Issue31065() + { + var carousel = new CarouselView + { + ItemsSource = new[] { "Item 1", "Item 2", "Item 3" }, + HeightRequest = 250, + }; + + _indicator = new IndicatorView + { + IndicatorsShape = IndicatorShape.Square, + SelectedIndicatorColor = Colors.Blue, + IndicatorSize = 30 + }; + + Button button = new Button + { + Text = "Change indicator shape", + HorizontalOptions = LayoutOptions.Center, + AutomationId = "ChangeIndicatorShapeButton", + Margin = new Thickness(0, 20, 0, 0) + }; + + button.Command = new Command(() => + { + if (_indicator.IndicatorsShape == IndicatorShape.Square) + { + _indicator.IndicatorsShape = IndicatorShape.Circle; + } + else + { + _indicator.IndicatorsShape = IndicatorShape.Square; + } + }); + + carousel.IndicatorView = _indicator; + + Content = new VerticalStackLayout + { + Children = + { + carousel, + _indicator, + button + } + }; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewCircleShape.png new file mode 100644 index 000000000000..d8c5e8ff64e3 Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewCircleShape.png differ diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewSquareShape.png new file mode 100644 index 000000000000..592ecf3603a3 Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewSquareShape.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31065.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31065.cs new file mode 100644 index 000000000000..454c8e842675 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31065.cs @@ -0,0 +1,30 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue31065 : _IssuesUITest +{ + public override string Issue => "IndicatorView square shape does not update on load or dynamically"; + + public Issue31065(TestDevice device) : base(device) + { } + + [Test, Order(0)] + [Category(UITestCategories.IndicatorView)] + public void UpdateIndicatorViewSquareShape() + { + App.WaitForElement("ChangeIndicatorShapeButton"); + VerifyScreenshot("IndicatorViewSquareShape"); + } + + [Test, Order(1)] + [Category(UITestCategories.IndicatorView)] + public void UpdateIndicatorViewCircleShape() + { + App.Tap("ChangeIndicatorShapeButton"); + VerifyScreenshot("IndicatorViewCircleShape"); + } + +} diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewCircleShape.png new file mode 100644 index 000000000000..71dd341017cb Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewCircleShape.png differ diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewSquareShape.png new file mode 100644 index 000000000000..bebabab51bef Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewSquareShape.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewCircleShape.png new file mode 100644 index 000000000000..5e972dd58de2 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewCircleShape.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewSquareShape.png new file mode 100644 index 000000000000..55e9fbf2ae37 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewSquareShape.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewCircleShape.png new file mode 100644 index 000000000000..afd0c35ad191 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewCircleShape.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewSquareShape.png new file mode 100644 index 000000000000..d9e625f1e13e Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewSquareShape.png differ diff --git a/src/Core/src/Platform/iOS/MauiPageControl.cs b/src/Core/src/Platform/iOS/MauiPageControl.cs index 97ba453ebcfc..011ab903b82b 100644 --- a/src/Core/src/Platform/iOS/MauiPageControl.cs +++ b/src/Core/src/Platform/iOS/MauiPageControl.cs @@ -10,7 +10,8 @@ public class MauiPageControl : UIPageControl, IUIViewLifeCycleEvents { const int DefaultIndicatorSize = 6; const double IndicatorSizeTolerance = 0.001; - + const string SquareSymbol = "squareshape.fill"; + const string CircleSymbol = "circlebadge.fill"; WeakReference? _indicatorView; bool _updatingPosition; double _lastAppliedIndicatorSize = -1; @@ -56,11 +57,10 @@ public override void LayoutSubviews() return; UpdateIndicatorSize(); - - if (!IsSquare) - return; - - UpdateSquareShape(); + if (_indicatorView?.TryGetTarget(out var indicatorView) == true && (indicatorView as ITemplatedIndicatorView)?.IndicatorsLayoutOverride == null) + { + UpdateIndicatorShape(); + } } public void UpdateIndicatorSize() @@ -107,9 +107,9 @@ public void UpdateIndicatorCount() UpdatePosition(); } - void UpdateSquareShape() + void UpdateIndicatorShape() { - if (!(OperatingSystem.IsIOSVersionAtLeast(14) || OperatingSystem.IsTvOSVersionAtLeast(14))) + if (!(OperatingSystem.IsIOSVersionAtLeast(14) || OperatingSystem.IsTvOSVersionAtLeast(14) || OperatingSystem.IsMacCatalystVersionAtLeast(14))) { UpdateCornerRadius(); return; @@ -118,20 +118,34 @@ void UpdateSquareShape() var uiPageControlContentView = Subviews[0]; if (uiPageControlContentView.Subviews.Length > 0) { - var uiPageControlIndicatorContentView = uiPageControlContentView.Subviews[0]; + foreach (var uiPageControlIndicatorContentView in uiPageControlContentView.Subviews) + { + SetIndicatorShape(uiPageControlIndicatorContentView, IsSquare); + } + } + } - foreach (var view in uiPageControlIndicatorContentView.Subviews) + // Recursively find UIImageView and set its image + static void SetIndicatorShape(UIView view, bool isSquare) + { + if (view is UIImageView imageView) + { + if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13) || OperatingSystem.IsMacCatalystVersionAtLeast(13)) { - if (view is UIImageView imageview) - { - if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13)) - imageview.Image = UIImage.GetSystemImage("squareshape.fill"); - var frame = imageview.Frame; - //the square shape is not the same size as the circle so we might need to correct the frame - imageview.Frame = new CGRect(frame.X - 6, frame.Y, frame.Width, frame.Height); - } + imageView.Image = UIImage.GetSystemImage(isSquare ? SquareSymbol : CircleSymbol); + return; } } + + if (view.Subviews is null || view.Subviews.Length == 0) + { + return; + } + + foreach (var child in view.Subviews) + { + SetIndicatorShape(child, isSquare); + } } void UpdateCornerRadius() @@ -150,7 +164,7 @@ void MauiPageControlValueChanged(object? sender, System.EventArgs e) indicatorView.Position = (int)CurrentPage; //if we are iOS13 or lower and we are using a Square shape //we need to update the CornerRadius of the new shape. - if (IsSquare && !(OperatingSystem.IsIOSVersionAtLeast(14) || OperatingSystem.IsTvOSVersionAtLeast(14))) + if (IsSquare && !(OperatingSystem.IsIOSVersionAtLeast(14) || OperatingSystem.IsTvOSVersionAtLeast(14) || OperatingSystem.IsMacCatalystVersionAtLeast(14))) LayoutSubviews(); }