Skip to content

Commit 970cb25

Browse files
authored
Implement iOS/macOS secondary ToolbarItems - Shell (#30480)
* Implement iOS/macOS secondary ToolbarItems - shell * Update ToolbarItemExtensions.cs
1 parent f29580f commit 970cb25

File tree

5 files changed

+74
-9
lines changed

5 files changed

+74
-9
lines changed

src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,22 +353,67 @@ protected virtual void UpdateToolbarItems()
353353

354354
var shellToolbarItems = _context?.Shell?.ToolbarItems;
355355
List<UIBarButtonItem>? primaries = null;
356+
List<UIMenuElement>? secondaries = null;
357+
356358
if (Page.ToolbarItems.Count > 0) // Display toolbar items defined on the current page
357359
{
358360
foreach (var item in System.Linq.Enumerable.OrderBy(Page.ToolbarItems, x => x.Priority))
359361
{
360-
(primaries = primaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem(false, true));
362+
if (item.Order == ToolbarItemOrder.Secondary)
363+
{
364+
(secondaries ??= []).Add(item.ToSecondarySubToolbarItem().PlatformAction);
365+
}
366+
else
367+
{
368+
(primaries ??= []).Add(item.ToUIBarButtonItem());
369+
}
361370
}
362371
}
363372
else if (shellToolbarItems != null && shellToolbarItems.Count > 0) // If the page has no toolbar items use the ones defined for the shell
364373
{
365374
foreach (var item in System.Linq.Enumerable.OrderBy(shellToolbarItems, x => x.Priority))
366375
{
367-
(primaries = primaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem(false, true));
376+
if (item.Order == ToolbarItemOrder.Secondary)
377+
{
378+
(secondaries ??= []).Add(item.ToSecondarySubToolbarItem().PlatformAction);
379+
}
380+
else
381+
{
382+
(primaries ??= []).Add(item.ToUIBarButtonItem());
383+
}
368384
}
369385
}
370386

371-
primaries?.Reverse();
387+
if (primaries is not null && primaries.Count > 0)
388+
{
389+
primaries.Reverse();
390+
}
391+
392+
if (secondaries is not null && secondaries.Count > 0)
393+
{
394+
UIImage? secondaryIcon = null;
395+
if (ViewController?.ParentViewController is ShellSectionRenderer ssr)
396+
{
397+
secondaryIcon = ssr.GetSecondaryToolbarMenuButtonImage();
398+
}
399+
else
400+
{
401+
// Shouldn't happen, but just in case let's add a fallback to the default icon
402+
secondaryIcon = UIImage.GetSystemImage("ellipsis.circle");
403+
}
404+
405+
var menu = UIMenu.Create(string.Empty, null, UIMenuIdentifier.Edit, UIMenuOptions.DisplayInline, secondaries.ToArray());
406+
var menuButton = new UIBarButtonItem(secondaryIcon, menu)
407+
{
408+
AccessibilityIdentifier = "SecondaryToolbarMenuButton"
409+
};
410+
411+
// Since we are adding secondary items under a primary button,
412+
// make sure that primaries is initialized
413+
primaries ??= [];
414+
415+
primaries.Insert(0, menuButton);
416+
}
372417

373418
NavigationItem.SetRightBarButtonItems(primaries is null ? Array.Empty<UIBarButtonItem>() : primaries.ToArray(), false);
374419

src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,19 @@ protected override void Dispose(bool disposing)
324324
base.Dispose(disposing);
325325
}
326326

327+
/// <summary>
328+
/// Override this method to provide a custom image for the secondary toolbar menu button.
329+
/// The default implementation uses the "ellipsis.circle" system image.
330+
/// This image is used for the menu button that appears when there are secondary toolbar items
331+
/// </summary>
332+
/// <returns>The image to use for the secondary toolbar menu button.</returns>
333+
public virtual UIImage GetSecondaryToolbarMenuButtonImage()
334+
{
335+
// Use the ellipsis.circle system image for the menu button by default
336+
// as per the iOS design guidelines: https://developer.apple.com/design/human-interface-guidelines/pull-down-buttons
337+
return UIImage.GetSystemImage("ellipsis.circle");
338+
}
339+
327340
protected virtual void HandleShellPropertyChanged(object sender, PropertyChangedEventArgs e)
328341
{
329342
if (e.Is(VisualElement.FlowDirectionProperty))

src/Controls/src/Core/Compatibility/iOS/Extensions/ToolbarItemExtensions.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ public static UIBarButtonItem ToUIBarButtonItem(this ToolbarItem item, bool forc
2929

3030
internal static SecondarySubToolbarItem ToSecondarySubToolbarItem(this ToolbarItem item)
3131
{
32+
var weakItem = new WeakReference<ToolbarItem>(item);
33+
3234
var action = UIAction.Create(item.Text, null, null, _ =>
3335
{
34-
if (item is IMenuItemController menuItemController)
35-
{
36-
menuItemController.Activate();
37-
}
38-
else
36+
if (weakItem.TryGetTarget(out var targetItem))
3937
{
40-
item.Command?.Execute(item.CommandParameter);
38+
if (targetItem is IMenuItemController menuItemController)
39+
{
40+
menuItemController.Activate();
41+
}
42+
else
43+
{
44+
targetItem.Command?.Execute(targetItem.CommandParameter);
45+
}
4146
}
4247
});
4348

src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,4 @@ virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.
368368
virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.UpdateSearchVisibility(UIKit.UISearchController! searchController) -> void
369369
~virtual Microsoft.Maui.Controls.PropertyChangingEventHandler.Invoke(object sender, Microsoft.Maui.Controls.PropertyChangingEventArgs e) -> void
370370
~virtual Microsoft.Maui.Controls.VisualElement.ComputeConstraintForView(Microsoft.Maui.Controls.View view) -> void
371+
~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.GetSecondaryToolbarMenuButtonImage() -> UIKit.UIImage

src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,4 @@ virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.
368368
virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.UpdateSearchVisibility(UIKit.UISearchController! searchController) -> void
369369
~virtual Microsoft.Maui.Controls.PropertyChangingEventHandler.Invoke(object sender, Microsoft.Maui.Controls.PropertyChangingEventArgs e) -> void
370370
~virtual Microsoft.Maui.Controls.VisualElement.ComputeConstraintForView(Microsoft.Maui.Controls.View view) -> void
371+
~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.GetSecondaryToolbarMenuButtonImage() -> UIKit.UIImage

0 commit comments

Comments
 (0)