From 709880275571889e9e26c8eee76930cae85386d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Sun, 16 Mar 2025 20:41:38 +0100 Subject: [PATCH 01/10] Enable Nullability on ShellPageRendererTracker --- .../Shell/iOS/ShellPageRendererTracker.cs | 296 ++++++++++++------ .../Handlers/Shell/iOS/UIContainerView.cs | 5 + .../PublicAPI/net-ios/PublicAPI.Unshipped.txt | 4 + .../net-maccatalyst/PublicAPI.Unshipped.txt | 4 + 4 files changed, 208 insertions(+), 101 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index 5b9a0418e20e..e74a08cc9aa7 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -1,15 +1,12 @@ -#nullable disable +#nullable enable // https://github.com/dotnet/maui/issues/27162 using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Runtime.Versioning; -using System.Threading.Tasks; using System.Windows.Input; using CoreGraphics; using Foundation; -using Microsoft.Extensions.Logging; -using ObjCRuntime; using UIKit; using static Microsoft.Maui.Controls.Compatibility.Platform.iOS.AccessibilityExtensions; using static Microsoft.Maui.Controls.Compatibility.Platform.iOS.ToolbarItemExtensions; @@ -22,15 +19,25 @@ public class ShellPageRendererTracker : IShellPageRendererTracker, IFlyoutBehavi public bool IsRootPage { get; set; } +#nullable disable public UIViewController ViewController { get { + if (_rendererRef is null) + return null; + _rendererRef.TryGetTarget(out var target); return target; } set { + if (value is null) + { + _rendererRef = null; + return; + } + _rendererRef = new WeakReference(value); OnRendererSet(); } @@ -50,27 +57,30 @@ public Page Page OnPageSet(oldPage, _page); } } +#nullable restore #endregion IShellPageRendererTracker - IShellContext _context; + IShellContext? _context; bool _disposed; FlyoutBehavior _flyoutBehavior; - WeakReference _rendererRef; - IShellSearchResultsRenderer _resultsRenderer; - UISearchController _searchController; - SearchHandler _searchHandler; - Page _page; + WeakReference? _rendererRef; + IShellSearchResultsRenderer? _resultsRenderer; + UISearchController? _searchController; + SearchHandler? _searchHandler; + Page? _page; NSCache _nSCache; - SearchHandlerAppearanceTracker _searchHandlerAppearanceTracker; + SearchHandlerAppearanceTracker? _searchHandlerAppearanceTracker; IFontManager _fontManager; bool _isVisiblePage; - BackButtonBehavior BackButtonBehavior { get; set; } - UINavigationItem NavigationItem { get; set; } - IMauiContext MauiContext => Page?.FindMauiContext() ?? _context.Shell.FindMauiContext(); + BackButtonBehavior? BackButtonBehavior { get; set; } + UINavigationItem? NavigationItem { get; set; } + IMauiContext? MauiContext => Page?.FindMauiContext() ?? _context?.Shell.FindMauiContext(); +#nullable disable public ShellPageRendererTracker(IShellContext context) +#nullable restore { _context = context; _nSCache = new NSCache(); @@ -85,21 +95,25 @@ public void OnFlyoutBehaviorChanged(FlyoutBehavior behavior) UpdateToolbarItemsInternal(); } +#nullable disable protected virtual void HandleShellPropertyChanged(object sender, PropertyChangedEventArgs e) { +#nullable restore if (e.Is(VisualElement.FlowDirectionProperty)) UpdateFlowDirection(); else if (e.Is(Shell.FlyoutIconProperty)) UpdateLeftToolbarItems(); } +#nullable disable protected virtual void OnBackButtonBehaviorPropertyChanged(object sender, PropertyChangedEventArgs e) { +#nullable restore if (e.PropertyName == BackButtonBehavior.CommandParameterProperty.PropertyName) return; else if (e.PropertyName == BackButtonBehavior.IsEnabledProperty.PropertyName) { - if (NavigationItem?.LeftBarButtonItem != null) + if (NavigationItem?.LeftBarButtonItem is not null && BackButtonBehavior is not null) NavigationItem.LeftBarButtonItem.Enabled = BackButtonBehavior.IsEnabled; return; @@ -108,8 +122,10 @@ protected virtual void OnBackButtonBehaviorPropertyChanged(object sender, Proper UpdateLeftToolbarItems(); } +#nullable disable protected virtual void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e) { +#nullable restore if (e.PropertyName == Shell.BackButtonBehaviorProperty.PropertyName) { SetBackButtonBehavior(Shell.GetBackButtonBehavior(Page)); @@ -147,7 +163,7 @@ protected virtual void UpdateTabBarVisible() } } - void OnToolbarPropertyChanged(object sender, PropertyChangedEventArgs e) + void OnToolbarPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (!ToolbarReady()) return; @@ -164,7 +180,7 @@ void OnToolbarPropertyChanged(object sender, PropertyChangedEventArgs e) protected virtual void UpdateTitle() { - if (!ToolbarReady()) + if (!ToolbarReady() || NavigationItem is null || _context?.Shell?.Toolbar is null) return; NavigationItem.Title = _context.Shell.Toolbar.Title; @@ -173,7 +189,7 @@ protected virtual void UpdateTitle() bool ToolbarReady() { - if (_context.Shell.Toolbar is ShellToolbar st) + if (_context?.Shell?.Toolbar is ShellToolbar st) return st.CurrentPage == Page; return _isVisiblePage; @@ -192,9 +208,11 @@ void UpdateShellToMyPage() UpdateToolbarItemsInternal(); } +#nullable disable protected virtual void OnPageSet(Page oldPage, Page newPage) +#nullable restore { - if (oldPage != null) + if (oldPage is not null) { oldPage.Appearing -= PageAppearing; oldPage.Disappearing -= PageDisappearing; @@ -203,7 +221,7 @@ protected virtual void OnPageSet(Page oldPage, Page newPage) ((INotifyCollectionChanged)oldPage.ToolbarItems).CollectionChanged -= OnToolbarItemsChanged; } - if (newPage != null) + if (newPage is not null) { newPage.Appearing += PageAppearing; newPage.Disappearing += PageDisappearing; @@ -217,7 +235,7 @@ protected virtual void OnPageSet(Page oldPage, Page newPage) if (oldPage == null) { - ((IShellController)_context.Shell).AddFlyoutBehaviorObserver(this); + (_context?.Shell as IShellController)?.AddFlyoutBehaviorObserver(this); } } else if (newPage == null && _context?.Shell is IShellController shellController) @@ -228,6 +246,9 @@ protected virtual void OnPageSet(Page oldPage, Page newPage) protected virtual void OnRendererSet() { + if (ViewController is null) + return; + NavigationItem = ViewController.NavigationItem; if (!(OperatingSystem.IsIOSVersionAtLeast(11) || OperatingSystem.IsTvOSVersionAtLeast(11))) @@ -238,10 +259,10 @@ protected virtual void OnRendererSet() protected virtual void UpdateTitleView() { - if (!ToolbarReady()) + if (!ToolbarReady() || NavigationItem is null) return; - var titleView = _context.Shell.Toolbar.TitleView as View; + var titleView = _context?.Shell?.Toolbar?.TitleView as View; if (NavigationItem.TitleView is TitleViewContainer tvc && tvc.View == titleView) @@ -254,11 +275,15 @@ protected virtual void UpdateTitleView() return; } - if (titleView == null) + if (titleView is null) { var view = NavigationItem.TitleView; NavigationItem.TitleView = null; - view?.Dispose(); + + if (view is UIContainerView uIContainerView) + uIContainerView.Disconnect(); + else + view?.Dispose(); } else { @@ -274,21 +299,26 @@ protected virtual void UpdateTitleView() } } - void OnTitleViewParentSet(object sender, EventArgs e) + void OnTitleViewParentSet(object? sender, EventArgs e) { - ((Element)sender).ParentSet -= OnTitleViewParentSet; + if (sender is Element element) + element.ParentSet -= OnTitleViewParentSet; + UpdateTitleView(); } internal void UpdateToolbarItemsInternal(bool updateWhenLoaded = true) { + if (Page is null) + return; + if (updateWhenLoaded && Page.IsLoaded || !updateWhenLoaded) UpdateToolbarItems(); } protected virtual void UpdateToolbarItems() { - if (NavigationItem == null) + if (NavigationItem is null || Page is null) { return; } @@ -300,7 +330,7 @@ protected virtual void UpdateToolbarItems() } var shellToolbarItems = _context?.Shell?.ToolbarItems; - List primaries = null; + List? primaries = null; if (Page.ToolbarItems.Count > 0) // Display toolbar items defined on the current page { foreach (var item in System.Linq.Enumerable.OrderBy(Page.ToolbarItems, x => x.Priority)) @@ -316,27 +346,32 @@ protected virtual void UpdateToolbarItems() } } - if (primaries != null) - primaries.Reverse(); + primaries?.Reverse(); - NavigationItem.SetRightBarButtonItems(primaries == null ? Array.Empty() : primaries.ToArray(), false); + NavigationItem.SetRightBarButtonItems(primaries is null ? Array.Empty() : primaries.ToArray(), false); UpdateLeftToolbarItems(); } void UpdateLeftToolbarItems() { + var shell = _context?.Shell; + var mauiContext = MauiContext; + + if (shell is null || NavigationItem is null || mauiContext is null) + return; + var behavior = BackButtonBehavior; - var image = behavior.GetPropertyIfSet(BackButtonBehavior.IconOverrideProperty, null); + var image = behavior.GetPropertyIfSet(BackButtonBehavior.IconOverrideProperty, null); var enabled = behavior.GetPropertyIfSet(BackButtonBehavior.IsEnabledProperty, true); - var text = behavior.GetPropertyIfSet(BackButtonBehavior.TextOverrideProperty, null); - var command = behavior.GetPropertyIfSet(BackButtonBehavior.CommandProperty, null); + var text = behavior.GetPropertyIfSet(BackButtonBehavior.TextOverrideProperty, null); + var command = behavior.GetPropertyIfSet(BackButtonBehavior.CommandProperty, null); var backButtonVisible = behavior.GetPropertyIfSet(BackButtonBehavior.IsVisibleProperty, true); if (String.IsNullOrWhiteSpace(text) && image == null) { - image = _context.Shell.FlyoutIcon; + image = shell.FlyoutIcon; } if (!IsRootPage) @@ -345,9 +380,12 @@ void UpdateLeftToolbarItems() image = backButtonVisible ? image : null; } - image.LoadImage(MauiContext, result => + image.LoadImage(mauiContext, result => { - UIImage icon = null; + if (ViewController is null) + return; + + UIImage? icon = null; if (image != null) { @@ -402,29 +440,36 @@ void UpdateLeftToolbarItems() void UpdateBackButtonTitle() { + if (ViewController is null) + return; + var behavior = BackButtonBehavior; - var text = behavior.GetPropertyIfSet(BackButtonBehavior.TextOverrideProperty, null); + var text = behavior.GetPropertyIfSet(BackButtonBehavior.TextOverrideProperty, null); var navController = ViewController?.NavigationController; if (navController != null) { - var viewControllers = ViewController.NavigationController.ViewControllers; - var count = viewControllers.Length; + var viewControllers = ViewController?.NavigationController?.ViewControllers; - if (count > 1 && viewControllers[count - 1] == ViewController) + if (viewControllers is not null) { - var previousNavItem = viewControllers[count - 2].NavigationItem; - if (previousNavItem != null) + var count = viewControllers.Length; + + if (count > 1 && viewControllers[count - 1] == ViewController) { - if (text is not null) + var previousNavItem = viewControllers[count - 2].NavigationItem; + if (previousNavItem != null) { - var barButtonItem = (previousNavItem.BackBarButtonItem ??= new UIBarButtonItem()); - barButtonItem.Title = text; - } - else if (previousNavItem.BackBarButtonItem != null) - { - previousNavItem.BackBarButtonItem = null; + if (text is not null) + { + var barButtonItem = (previousNavItem.BackBarButtonItem ??= new UIBarButtonItem()); + barButtonItem.Title = text; + } + else if (previousNavItem.BackBarButtonItem != null) + { + previousNavItem.BackBarButtonItem = null; + } } } } @@ -435,10 +480,10 @@ void LeftBarButtonItemHandler(UIViewController controller, bool isRootPage) { var behavior = BackButtonBehavior; - var command = behavior.GetPropertyIfSet(BackButtonBehavior.CommandProperty, null); - var commandParameter = behavior.GetPropertyIfSet(BackButtonBehavior.CommandParameterProperty, null); + var command = behavior.GetPropertyIfSet(BackButtonBehavior.CommandProperty, null); + var commandParameter = behavior.GetPropertyIfSet(BackButtonBehavior.CommandParameterProperty, null); - if (command != null) + if (command is not null) { command.Execute(commandParameter); } @@ -451,7 +496,7 @@ void LeftBarButtonItemHandler(UIViewController controller, bool isRootPage) } else if (_flyoutBehavior == FlyoutBehavior.Flyout) { - _context.Shell.SetValueFromRenderer(Shell.FlyoutIsPresentedProperty, true); + _context?.Shell?.SetValueFromRenderer(Shell.FlyoutIsPresentedProperty, true); } } @@ -492,7 +537,7 @@ UIImage DrawHamburger() return img; } - void OnToolbarItemsChanged(object sender, NotifyCollectionChangedEventArgs e) + void OnToolbarItemsChanged(object? sender, NotifyCollectionChangedEventArgs e) { UpdateToolbarItemsInternal(); } @@ -528,7 +573,9 @@ void OnBackButtonCommandCanExecuteChanged(object sender, EventArgs e) public class TitleViewContainer : UIContainerView { +#nullable disable public TitleViewContainer(View view) : base(view) +#nullable restore { MatchHeight = true; @@ -564,13 +611,13 @@ public override void LayoutSubviews() base.LayoutSubviews(); } - public override void WillMoveToSuperview(UIView newSuper) + public override void WillMoveToSuperview(UIView? newSuper) { UpdateFrame(newSuper); base.WillMoveToSuperview(newSuper); } - void UpdateFrame(UIView newSuper) + void UpdateFrame(UIView? newSuper) { if (newSuper is not null && newSuper.Bounds != CGRect.Empty) { @@ -588,7 +635,7 @@ public override CGSize SizeThatFits(CGSize size) #region SearchHandler - SearchHandler SearchHandler + SearchHandler? SearchHandler { get { return _searchHandler; } set @@ -618,8 +665,13 @@ SearchHandler SearchHandler } } +#nullable disable protected virtual void OnSearchHandlerPropertyChanged(object sender, PropertyChangedEventArgs e) +#nullable restore { + if (_searchHandler is null || _searchController is null) + return; + if (e.PropertyName == SearchHandler.ClearPlaceholderEnabledProperty.PropertyName) _searchController.SearchBar.ShowsBookmarkButton = _searchHandler.ClearPlaceholderEnabled; else if (e.PropertyName == SearchHandler.SearchBoxVisibilityProperty.PropertyName) @@ -659,11 +711,17 @@ protected virtual void RemoveSearchController(UINavigationItem navigationItem) protected virtual void UpdateSearchIsEnabled(UISearchController searchController) { + if (SearchHandler is null) + return; + searchController.SearchBar.UserInteractionEnabled = SearchHandler.IsSearchEnabled; } protected virtual void UpdateSearchVisibility(UISearchController searchController) { + if (SearchHandler is null || NavigationItem is null) + return; + var visibility = SearchHandler.SearchBoxVisibility; if (visibility == SearchBoxVisibility.Hidden) { @@ -679,31 +737,36 @@ protected virtual void UpdateSearchVisibility(UISearchController searchControlle { if (OperatingSystem.IsIOSVersionAtLeast(11)) { - NavigationItem.SearchController = _searchController; + NavigationItem.SearchController = searchController; NavigationItem.HidesSearchBarWhenScrolling = visibility == SearchBoxVisibility.Collapsible; } else { - NavigationItem.TitleView = _searchController.SearchBar; + NavigationItem.TitleView = searchController.SearchBar; } } } void UpdateFlowDirection() { - if (_searchHandlerAppearanceTracker != null) - { - _searchHandlerAppearanceTracker.UpdateFlowDirection(_context.Shell); - } - if (_searchController != null) + var shell = _context?.Shell; + + if (shell is null) + return; + + _searchHandlerAppearanceTracker?.UpdateFlowDirection(shell); + + if (_searchController?.View is not null) { - _searchController.View.UpdateFlowDirection(_context.Shell); - _searchController.SearchBar.UpdateFlowDirection(_context.Shell); + _searchController.View.UpdateFlowDirection(shell); + _searchController.SearchBar.UpdateFlowDirection(shell); } } void AttachSearchController() { + if (SearchHandler is null || ViewController is null || NavigationItem is null || _context is null) + return; if (SearchHandler.ShowsResults) { @@ -764,44 +827,64 @@ void AttachSearchController() UpdateAutomationId(); } - void BookmarkButtonClicked(object sender, EventArgs e) + void BookmarkButtonClicked(object? sender, EventArgs e) { - ((ISearchHandlerController)SearchHandler).ClearPlaceholderClicked(); + ((ISearchHandlerController?)SearchHandler)?.ClearPlaceholderClicked(); } void DettachSearchController() { - _searchHandlerAppearanceTracker.Dispose(); - _searchHandlerAppearanceTracker = null; - if (OperatingSystem.IsIOSVersionAtLeast(11)) + if (_searchHandlerAppearanceTracker is not null) { - RemoveSearchController(NavigationItem); + _searchHandlerAppearanceTracker.Dispose(); + _searchHandlerAppearanceTracker = null; } - else + + if (NavigationItem is not null) { - NavigationItem.TitleView = null; + if (OperatingSystem.IsIOSVersionAtLeast(11)) + { + RemoveSearchController(NavigationItem); + } + else + { + NavigationItem.TitleView = null; + } } - _searchController.SetSearchResultsUpdater(null); - _searchController.Dispose(); - _searchController = null; + if (_searchController is not null) + { + _searchController.SetSearchResultsUpdater(_ => { }); + _searchController = null; + } } - void OnSearchItemSelected(object sender, object e) + void OnSearchItemSelected(object? sender, object e) { + if (_searchController is null) + return; + _searchController.Active = false; - ((ISearchHandlerController)SearchHandler).ItemSelected(e); + ((ISearchHandlerController?)SearchHandler)?.ItemSelected(e); } - void SearchButtonClicked(object sender, EventArgs e) + void SearchButtonClicked(object? sender, EventArgs e) { - ((ISearchHandlerController)SearchHandler).QueryConfirmed(); + ((ISearchHandlerController?)SearchHandler)?.QueryConfirmed(); } void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon icon) { - source.LoadImage(source.FindMauiContext(), image => + var mauiContext = source.FindMauiContext() ?? MauiContext; + + if (mauiContext is null) + return; + + source.LoadImage(mauiContext, image => { + if (_disposed) + return; + var result = image?.Value; if (result != null) { @@ -813,7 +896,7 @@ void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon }); } - void OnPageLoaded(object sender, EventArgs e) + void OnPageLoaded(object? sender, EventArgs e) { if (sender is Page page) page.Loaded -= OnPageLoaded; @@ -829,15 +912,15 @@ void OnPageLoaded(object sender, EventArgs e) CheckAppeared(); } - void PageAppearing(object sender, EventArgs e) => + void PageAppearing(object? sender, EventArgs e) => SetAppeared(); - void PageDisappearing(object sender, EventArgs e) => + void PageDisappearing(object? sender, EventArgs e) => SetDisappeared(); void CheckAppeared() { - if (_context.Shell.CurrentPage == Page) + if (_context?.Shell?.CurrentPage == Page) SetAppeared(); } @@ -852,7 +935,7 @@ void SetAppeared() _searchHandlerAppearanceTracker?.UpdateSearchBarColors(); UpdateShellToMyPage(); - if (_context.Shell.Toolbar != null) + if (_context?.Shell?.Toolbar is not null) _context.Shell.Toolbar.PropertyChanged += OnToolbarPropertyChanged; } @@ -888,20 +971,31 @@ protected virtual void Dispose(bool disposing) if (disposing) { _searchHandlerAppearanceTracker?.Dispose(); - Page.Loaded -= OnPageLoaded; - Page.Appearing -= PageAppearing; - Page.Disappearing -= PageDisappearing; - Page.PropertyChanged -= OnPagePropertyChanged; - ((INotifyCollectionChanged)Page.ToolbarItems).CollectionChanged -= OnToolbarItemsChanged; - ((IShellController)_context.Shell).RemoveFlyoutBehaviorObserver(this); - if (BackButtonBehavior != null) - BackButtonBehavior.PropertyChanged -= OnBackButtonBehaviorPropertyChanged; + if (Page is not null) + { + Page.Loaded -= OnPageLoaded; + Page.Appearing -= PageAppearing; + Page.Disappearing -= PageDisappearing; + Page.PropertyChanged -= OnPagePropertyChanged; + ((INotifyCollectionChanged)Page.ToolbarItems).CollectionChanged -= OnToolbarItemsChanged; + } + + var shell = _context?.Shell; + + if (shell is not null) + { + + ((IShellController)shell).RemoveFlyoutBehaviorObserver(this); - _context.Shell.PropertyChanged -= HandleShellPropertyChanged; + if (BackButtonBehavior is not null) + BackButtonBehavior.PropertyChanged -= OnBackButtonBehaviorPropertyChanged; - if (_context.Shell.Toolbar != null) - _context.Shell.Toolbar.PropertyChanged -= OnToolbarPropertyChanged; + shell.PropertyChanged -= HandleShellPropertyChanged; + + if (shell.Toolbar is not null) + shell.Toolbar.PropertyChanged -= OnToolbarPropertyChanged; + } if (NavigationItem?.TitleView is TitleViewContainer tvc) tvc.Disconnect(); @@ -918,4 +1012,4 @@ protected virtual void Dispose(bool disposing) } #endregion IDisposable Support } -} +} \ No newline at end of file diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs index 8791278dd497..6460eac9f763 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs @@ -135,6 +135,11 @@ public override void LayoutSubviews() internal void Disconnect() { + if (_platformView?.Superview == this) + _platformView.RemoveFromSuperview(); + + _renderer = null; + _platformView = null; } protected override void Dispose(bool disposing) diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index caa05b5e4c9e..c3e24cef6304 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -389,3 +389,7 @@ Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.UIModalPresentationSty *REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.SetNeedsLayout() -> void *REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.MovedToWindow() -> void override Microsoft.Maui.Controls.Handlers.Compatibility.VisualElementRenderer.MovedToWindow() -> void +virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.UpdateSearchIsEnabled(UIKit.UISearchController! searchController) -> void +virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.UpdateSearchVisibility(UIKit.UISearchController! searchController) -> void +virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.RemoveSearchController(UIKit.UINavigationItem! navigationItem) -> void +override Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.TitleViewContainer.WillMoveToSuperview(UIKit.UIView? newSuper) -> void \ No newline at end of file diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index d156363249e9..32f81759d78c 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -389,3 +389,7 @@ Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.UIModalPresentationSty *REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.SetNeedsLayout() -> void *REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.MovedToWindow() -> void override Microsoft.Maui.Controls.Handlers.Compatibility.VisualElementRenderer.MovedToWindow() -> void +virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.UpdateSearchIsEnabled(UIKit.UISearchController! searchController) -> void +virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.UpdateSearchVisibility(UIKit.UISearchController! searchController) -> void +virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.RemoveSearchController(UIKit.UINavigationItem! navigationItem) -> void +override Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.TitleViewContainer.WillMoveToSuperview(UIKit.UIView? newSuper) -> void \ No newline at end of file From b9f0e87a0d4d621971800edaf5133893b9ab161b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Sua=CC=81rez?= Date: Wed, 21 May 2025 11:47:27 +0200 Subject: [PATCH 02/10] Fix broken tests --- .../Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs index af44b2e29dd9..eab6ab7b8e0d 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs @@ -137,11 +137,7 @@ public override void LayoutSubviews() internal void Disconnect() { - if (_platformView?.Superview == this) - _platformView.RemoveFromSuperview(); - _renderer = null; - _platformView = null; } protected override void Dispose(bool disposing) From 1ff5fd9f4c3e87c2be9d25b21f6c71a1a7b11f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Sua=CC=81rez?= Date: Wed, 21 May 2025 11:53:10 +0200 Subject: [PATCH 03/10] Remove unnecessary changes --- .../src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs index eab6ab7b8e0d..7484b56266a8 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerView.cs @@ -137,7 +137,6 @@ public override void LayoutSubviews() internal void Disconnect() { - } protected override void Dispose(bool disposing) From 0de33e7da1a4c98cc7ba22dfa648a6e1fe8135af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 28 May 2025 13:42:27 +0200 Subject: [PATCH 04/10] Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellPageRendererTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index a00c7a588f0d..0447727758e9 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -719,7 +719,9 @@ protected virtual void RemoveSearchController(UINavigationItem navigationItem) protected virtual void UpdateSearchIsEnabled(UISearchController searchController) { if (SearchHandler is null) + { return; + } searchController.SearchBar.UserInteractionEnabled = SearchHandler.IsSearchEnabled; } From 297944638f694171389b3c30ebf985c46e2279f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 28 May 2025 13:42:57 +0200 Subject: [PATCH 05/10] Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellPageRendererTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index 0447727758e9..735bf2617d5e 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -887,7 +887,9 @@ void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon var mauiContext = source.FindMauiContext() ?? MauiContext; if (mauiContext is null) + { return; + } source.LoadImage(mauiContext, image => { From 827ef70b03952e7d3b6e6f82d62ae22f687ea946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 28 May 2025 13:43:07 +0200 Subject: [PATCH 06/10] Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellPageRendererTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index 735bf2617d5e..2c6677a26aec 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -729,7 +729,9 @@ protected virtual void UpdateSearchIsEnabled(UISearchController searchController protected virtual void UpdateSearchVisibility(UISearchController searchController) { if (SearchHandler is null || NavigationItem is null) + { return; + } var visibility = SearchHandler.SearchBoxVisibility; if (visibility == SearchBoxVisibility.Hidden) From 7a838c4aab0f7bc3b55955d3593b1c7301bf7ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 28 May 2025 13:43:20 +0200 Subject: [PATCH 07/10] Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellPageRendererTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index 2c6677a26aec..2417bd078ef6 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -763,7 +763,9 @@ void UpdateFlowDirection() var shell = _context?.Shell; if (shell is null) + { return; + } _searchHandlerAppearanceTracker?.UpdateFlowDirection(shell); From 8f2aa79d1293ef4a1f35772a8925f772b3847c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 28 May 2025 13:43:38 +0200 Subject: [PATCH 08/10] Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellPageRendererTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index 2417bd078ef6..037fc0e7aabf 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -875,7 +875,9 @@ void DettachSearchController() void OnSearchItemSelected(object? sender, object e) { if (_searchController is null) + { return; + } _searchController.Active = false; ((ISearchHandlerController?)SearchHandler)?.ItemSelected(e); From 66af9a1a192007ad694c8472aad086699a51ae28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 28 May 2025 13:45:01 +0200 Subject: [PATCH 09/10] Update src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Co-authored-by: Rui Marinho --- .../Handlers/Shell/iOS/ShellPageRendererTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index 037fc0e7aabf..d9512f20fe20 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -900,7 +900,9 @@ void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon source.LoadImage(mauiContext, image => { if (_disposed) + { return; + } var result = image?.Value; if (result != null) From 7849431130f90584c3edba2629b1376ba2fea916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 27 May 2025 23:15:32 +0200 Subject: [PATCH 10/10] Changes based on feedback --- .../Shell/iOS/ShellPageRendererTracker.cs | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs index d9512f20fe20..c5d33bf01428 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs @@ -25,7 +25,9 @@ public UIViewController ViewController get { if (_rendererRef is null) + { return null; + } _rendererRef.TryGetTarget(out var target); return target; @@ -49,7 +51,9 @@ public Page Page set { if (_page == value) + { return; + } var oldPage = _page; _page = value; @@ -166,7 +170,9 @@ protected virtual void UpdateTabBarVisible() void OnToolbarPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (!ToolbarReady()) + { return; + } if (e.PropertyName == Shell.TitleViewProperty.PropertyName) { @@ -181,7 +187,9 @@ void OnToolbarPropertyChanged(object? sender, PropertyChangedEventArgs e) protected virtual void UpdateTitle() { if (!ToolbarReady() || NavigationItem is null || _context?.Shell?.Toolbar is null) + { return; + } NavigationItem.Title = _context.Shell.Toolbar.Title; } @@ -190,7 +198,9 @@ protected virtual void UpdateTitle() bool ToolbarReady() { if (_context?.Shell?.Toolbar is ShellToolbar st) + { return st.CurrentPage == Page; + } return _isVisiblePage; } @@ -198,7 +208,9 @@ bool ToolbarReady() void UpdateShellToMyPage() { if (Page == null) + { return; + } SetBackButtonBehavior(Shell.GetBackButtonBehavior(Page)); SearchHandler = Shell.GetSearchHandler(Page); @@ -247,7 +259,9 @@ protected virtual void OnPageSet(Page oldPage, Page newPage) protected virtual void OnRendererSet() { if (ViewController is null) + { return; + } NavigationItem = ViewController.NavigationItem; @@ -260,7 +274,9 @@ protected virtual void OnRendererSet() protected virtual void UpdateTitleView() { if (!ToolbarReady() || NavigationItem is null) + { return; + } var titleView = _context?.Shell?.Toolbar?.TitleView as View; @@ -302,7 +318,9 @@ protected virtual void UpdateTitleView() void OnTitleViewParentSet(object? sender, EventArgs e) { if (sender is Element element) + { element.ParentSet -= OnTitleViewParentSet; + } UpdateTitleView(); } @@ -310,10 +328,14 @@ void OnTitleViewParentSet(object? sender, EventArgs e) internal void UpdateToolbarItemsInternal(bool updateWhenLoaded = true) { if (Page is null) + { return; + } if (updateWhenLoaded && Page.IsLoaded || !updateWhenLoaded) + { UpdateToolbarItems(); + } } protected virtual void UpdateToolbarItems() @@ -359,7 +381,9 @@ void UpdateLeftToolbarItems() var mauiContext = MauiContext; if (shell is null || NavigationItem is null || mauiContext is null) + { return; + } var behavior = BackButtonBehavior; @@ -441,7 +465,9 @@ void UpdateLeftToolbarItems() void UpdateBackButtonTitle() { if (ViewController is null) + { return; + } var behavior = BackButtonBehavior; var text = behavior.GetPropertyIfSet(BackButtonBehavior.TextOverrideProperty, null); @@ -648,7 +674,9 @@ public override CGSize SizeThatFits(CGSize size) set { if (_searchHandler == value) + { return; + } if (_searchHandler != null) { @@ -677,7 +705,9 @@ protected virtual void OnSearchHandlerPropertyChanged(object sender, PropertyCha #nullable restore { if (_searchHandler is null || _searchController is null) + { return; + } if (e.PropertyName == SearchHandler.ClearPlaceholderEnabledProperty.PropertyName) _searchController.SearchBar.ShowsBookmarkButton = _searchHandler.ClearPlaceholderEnabled; @@ -694,7 +724,9 @@ protected virtual void OnSearchHandlerPropertyChanged(object sender, PropertyCha void UpdateAutomationId() { if (_searchHandler?.AutomationId != null && _searchController?.SearchBar != null) + { _searchController.SearchBar.AccessibilityIdentifier = _searchHandler.AutomationId; + } } [SupportedOSPlatform("ios11.0")] @@ -842,7 +874,7 @@ void AttachSearchController() void BookmarkButtonClicked(object? sender, EventArgs e) { - ((ISearchHandlerController?)SearchHandler)?.ClearPlaceholderClicked(); + (SearchHandler as ISearchHandlerController)?.ClearPlaceholderClicked(); } void DettachSearchController() @@ -880,12 +912,12 @@ void OnSearchItemSelected(object? sender, object e) } _searchController.Active = false; - ((ISearchHandlerController?)SearchHandler)?.ItemSelected(e); + (SearchHandler as ISearchHandlerController)?.ItemSelected(e); } void SearchButtonClicked(object? sender, EventArgs e) { - ((ISearchHandlerController?)SearchHandler)?.QueryConfirmed(); + (SearchHandler as ISearchHandlerController)?.QueryConfirmed(); } void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon icon) @@ -918,7 +950,9 @@ void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon void OnPageLoaded(object? sender, EventArgs e) { if (sender is Page page) + { page.Loaded -= OnPageLoaded; + } // This means the user removed this page during the loaded event if (_page is null) @@ -940,13 +974,17 @@ void PageDisappearing(object? sender, EventArgs e) => void CheckAppeared() { if (_context?.Shell?.CurrentPage == Page) + { SetAppeared(); + } } void SetAppeared() { if (_isVisiblePage) + { return; + } _isVisiblePage = true; //UIKIt will try to override our colors when the SearchController is inside the NavigationBar @@ -955,13 +993,17 @@ void SetAppeared() UpdateShellToMyPage(); if (_context?.Shell?.Toolbar is not null) + { _context.Shell.Toolbar.PropertyChanged += OnToolbarPropertyChanged; + } } void SetDisappeared() { if (!_isVisiblePage) + { return; + } _isVisiblePage = false; @@ -985,7 +1027,9 @@ public void Dispose() protected virtual void Dispose(bool disposing) { if (_disposed) + { return; + } if (disposing) { @@ -1004,7 +1048,6 @@ protected virtual void Dispose(bool disposing) if (shell is not null) { - ((IShellController)shell).RemoveFlyoutBehaviorObserver(this); if (BackButtonBehavior is not null)