Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using CoreGraphics;
using Foundation;
using Microsoft.Maui.Controls.Diagnostics;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
Expand Down Expand Up @@ -54,6 +55,7 @@ public class NavigationRenderer : UINavigationController, INavigationViewHandler
WeakReference<VisualElement> _element;
WeakReference<Page> _current;
bool _uiRequestedPop; // User tapped the back button or swiped to navigate back
readonly NativeElementRegistrationSet _nativeNavigationRegistrations = new NativeElementRegistrationSet();
MauiNavigationDelegate NavigationDelegate => Delegate as MauiNavigationDelegate;

[Internals.Preserve(Conditional = true)]
Expand Down Expand Up @@ -94,8 +96,17 @@ public UIView NativeView

public void SetElement(VisualElement element)
{
_nativeNavigationRegistrations.Clear();
(this as IElementHandler).SetVirtualView(element);
_element = element is null ? null : new(element);
if (element is NavigationPage navigationPage)
{
_nativeNavigationRegistrations.Register(
navigationPage,
NavigationBar,
NativeElementRoles.Toolbar,
NativeElementDiscriminators.RealizedView);
}
}

public UIViewController ViewController
Expand Down Expand Up @@ -275,6 +286,7 @@ protected override void Dispose(bool disposing)

if (disposing)
{
_nativeNavigationRegistrations.Clear();
Delegate = null;
foreach (var childViewController in ViewControllers)
childViewController.Dispose();
Expand Down Expand Up @@ -1389,6 +1401,7 @@ class ParentingViewController : UIViewController
bool _disposed;
ToolbarTracker _tracker = new ToolbarTracker();
List<ToolbarItem> _trackedToolbarItems = new List<ToolbarItem>();
readonly NativeElementRegistrationSet _nativeToolbarRegistrations = new NativeElementRegistrationSet();
bool _toolbarUpdatePending = false;

public ParentingViewController(NavigationRenderer navigation)
Expand Down Expand Up @@ -1560,6 +1573,7 @@ public override void WillMoveToParentViewController(UIViewController parent)

internal void Disconnect(bool dispose)
{
_nativeToolbarRegistrations.Clear();
// Unsubscribe from toolbar item property changes
CleanToolbarItems();

Expand Down Expand Up @@ -2012,6 +2026,7 @@ void CleanToolbarItems()

void UpdateToolbarItems()
{
_nativeToolbarRegistrations.Clear();
// Unsubscribe from previous toolbar item property changes
CleanToolbarItems();

Expand Down Expand Up @@ -2043,11 +2058,23 @@ void UpdateToolbarItems()

if (item.Order == ToolbarItemOrder.Secondary)
{
(secondaries ??= []).Add(item.ToSecondarySubToolbarItem().PlatformAction);
var secondaryItem = item.ToSecondarySubToolbarItem().PlatformAction;
(secondaries ??= []).Add(secondaryItem);
_nativeToolbarRegistrations.Register(
item,
secondaryItem,
NativeElementRoles.ToolbarOverflow,
NativeElementDiscriminators.LogicalModel);
}
else
{
(primaries ??= []).Add(item.ToUIBarButtonItem());
var primaryItem = item.ToUIBarButtonItem();
(primaries ??= []).Add(primaryItem);
_nativeToolbarRegistrations.Register(
item,
primaryItem,
NativeElementRoles.ToolbarItem,
NativeElementDiscriminators.LogicalModel);
}
}

Expand Down Expand Up @@ -2080,6 +2107,19 @@ void UpdateToolbarItems()
primaries ??= [];

primaries.Insert(0, menuButton);
if (Child is Page child)
{
_nativeToolbarRegistrations.Register(
child,
menu,
NativeElementRoles.ToolbarOverflow,
NativeElementDiscriminators.LogicalModel);
_nativeToolbarRegistrations.Register(
child,
menuButton,
NativeElementRoles.ToolbarOverflow,
NativeElementDiscriminators.LogicalModel);
}
}

NavigationItem.SetRightBarButtonItems(primaries is null ? [] : primaries.ToArray(), false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
using System.Linq;
using CoreGraphics;
using Foundation;
using Microsoft.Maui.Controls.Diagnostics;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Diagnostics;
using Microsoft.Maui.Graphics;
using ObjCRuntime;
using UIKit;
Expand All @@ -15,8 +17,6 @@ namespace Microsoft.Maui.Controls.Platform.Compatibility
{
public class ShellItemRenderer : UITabBarController, IShellItemRenderer, IAppearanceObserver, IUINavigationControllerDelegate, IDisconnectable
{
readonly static UITableViewCell[] EmptyUITableViewCellArray = Array.Empty<UITableViewCell>();

#region IShellItemRenderer

public ShellItem ShellItem
Expand Down Expand Up @@ -55,6 +55,11 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
bool _disposed;
ShellItem _shellItem;
static UIColor _defaultMoreTextLabelTextColor;
readonly NativeElementRegistrationSet _nativeTabBarRegistrations = new NativeElementRegistrationSet();
readonly NativeElementRegistrationSet _nativeVisibleTabRegistrations = new NativeElementRegistrationSet();
readonly NativeElementRegistrationSet _nativeMoreRegistrations = new NativeElementRegistrationSet();
UITableView _moreTableView;
IDisposable _moreContentOffsetObserver;

internal IShellSectionRenderer CurrentRenderer { get; private set; }

Expand All @@ -70,6 +75,8 @@ public override UIViewController SelectedViewController
set
{
base.SelectedViewController = value;
if (_disposed)
return;

var renderer = RendererForViewController(value);
if (renderer != null)
Expand All @@ -91,6 +98,9 @@ public override UIViewController SelectedViewController
[Preserve(AllMembers = true)]
public virtual void DidShowViewController(UINavigationController navigationController, [Transient] UIViewController viewController, bool animated)
{
if (_disposed)
return;

var renderer = RendererForViewController(this.SelectedViewController);
if (renderer != null)
{
Expand All @@ -117,11 +127,13 @@ public override void ViewDidLayoutSubviews()

_appearanceTracker?.UpdateLayout(this);
UpdateNavBarHidden();
RegisterVisibleTabViews();
}

public override void ViewDidLoad()
{
base.ViewDidLoad();
RegisterTabBar();

ShouldSelectViewController = (tabController, viewController) =>
{
Expand All @@ -143,9 +155,22 @@ public override void ViewDidLoad()
};
}

void RegisterTabBar()
{
if (ShellItem is null || !IsViewLoaded)
return;

_nativeTabBarRegistrations.Register(
ShellItem,
TabBar,
NativeElementRoles.ShellTab,
NativeElementDiscriminators.TabBar);
}

public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
RegisterTabBar();
ApplyInitialDisabledState();
}

Expand Down Expand Up @@ -205,6 +230,15 @@ UIImage CreateTintedImage(UIImage image, UIColor color)

void IDisconnectable.Disconnect()
{
_nativeTabBarRegistrations.Clear();
_nativeVisibleTabRegistrations.Clear();
_nativeMoreRegistrations.Clear();
_moreContentOffsetObserver?.Dispose();
_moreContentOffsetObserver = null;
_moreTableView = null;
if (ReferenceEquals(MoreNavigationController.WeakDelegate, this))
MoreNavigationController.WeakDelegate = null;

if (_sectionRenderers != null)
{
foreach (var kvp in _sectionRenderers.ToList())
Expand Down Expand Up @@ -269,6 +303,8 @@ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEv

protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
_nativeMoreRegistrations.Clear();

if (e.OldItems != null)
{
foreach (ShellSection shellSection in e.OldItems)
Expand Down Expand Up @@ -327,6 +363,9 @@ protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionC
}

UpdateTabBarHidden();
View.SetNeedsLayout();
if (SelectedViewController == MoreNavigationController)
UpdateMoreCellsEnabled();
}

protected virtual void OnShellItemSet(ShellItem shellItem)
Expand Down Expand Up @@ -443,18 +482,51 @@ void CreateTabRenderers()

void UpdateMoreCellsEnabled()
{
var moreNavigationCells = GetMoreNavigationCells();
var viewControllersLength = ViewControllers.Length;
// now that they are applied we can set the enabled state of the TabBar items
for (int i = 4; i < viewControllersLength; i++)
if (_disposed || ShellItem is null)
return;

var moreTableView = MoreNavigationController.TopViewController.View as UITableView;
if (!ReferenceEquals(_moreTableView, moreTableView))
{
if ((i - 4) >= (moreNavigationCells.Length))
_moreContentOffsetObserver?.Dispose();
_moreTableView = moreTableView;
if (_moreTableView is not null)
{
break;
_moreContentOffsetObserver = _moreTableView.AddObserver(
"contentOffset",
NSKeyValueObservingOptions.New,
_ => OnMoreTableScrolled());
}
}

var renderer = RendererForViewController(ViewControllers[i]);
var cell = moreNavigationCells[i - 4];
if (moreTableView?.Window is null)
{
_nativeMoreRegistrations.Clear();
return;
}

var viewControllersLength = ViewControllers.Length;
var retainedCells = new List<object>();
// now that they are applied we can set the enabled state of the TabBar items
foreach (var cell in moreTableView.VisibleCells)
{
var indexPath = moreTableView.IndexPathForCell(cell);
if (indexPath is null)
continue;

var sectionIndex = 4 + (int)indexPath.Row;
if (sectionIndex < 4 || sectionIndex >= viewControllersLength)
continue;

var renderer = RendererForViewController(ViewControllers[sectionIndex]);
if (renderer is null)
continue;
retainedCells.Add(cell);
_nativeMoreRegistrations.Register(
renderer.ShellSection,
cell,
NativeElementRoles.ShellTabOverflow,
NativeElementDiscriminators.OverflowRow);

#pragma warning disable CA1416, CA1422 // TODO: 'UITableViewCell.TextLabel' is unsupported on: 'ios' 14.0 and later
if (!renderer.ShellSection.IsEnabled)
Expand All @@ -473,14 +545,66 @@ void UpdateMoreCellsEnabled()
}
#pragma warning restore CA1416, CA1422
}
_nativeMoreRegistrations.Retain(retainedCells);
}

void OnMoreTableScrolled()
{
if (_disposed)
return;

UpdateMoreCellsEnabled();
}

UITableViewCell[] GetMoreNavigationCells()
void RegisterVisibleTabViews()
{
if (!NativeElementDiagnostics.IsRegistrationEnabled)
{
if (_nativeVisibleTabRegistrations.HasRegistrations)
_nativeVisibleTabRegistrations.Clear();
return;
}

if (ShellItem is null)
return;

var sections = ShellItemController.GetItems();
var controls = TabBar.Subviews
.OfType<UIControl>()
.OrderBy(control => control.Frame.X)
.ToList();
if (TabBar.EffectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft)
controls.Reverse();

var retainedElements = new List<object>(controls.Count + 1);
var hasMore = sections.Count > controls.Count;
for (int index = 0; index < controls.Count; index++)
{
if (MoreNavigationController.TopViewController.View is UITableView uITableView && uITableView.Window is not null)
return uITableView.VisibleCells;
var isMore = hasMore && index == controls.Count - 1;
if (!isMore && index >= sections.Count)
continue;

object owner = isMore ? ShellItem : sections[index];
var control = controls[index];
retainedElements.Add(control);
_nativeVisibleTabRegistrations.RegisterExclusive(
owner,
control,
isMore ? NativeElementRoles.ShellTabOverflow : NativeElementRoles.ShellTab,
NativeElementDiscriminators.RealizedView);
}

return EmptyUITableViewCellArray;
if (hasMore && TabBar.Items?.LastOrDefault() is UITabBarItem moreItem)
{
retainedElements.Add(moreItem);
_nativeVisibleTabRegistrations.Register(
ShellItem,
moreItem,
NativeElementRoles.ShellTabOverflow,
NativeElementDiscriminators.TabBarItem);
}

_nativeVisibleTabRegistrations.Retain(retainedElements);
}

void GoTo(ShellSection shellSection)
Expand Down
Loading
Loading