From 4e0a3a28450019db18622bc0a6b41fdcf7de547b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 12:58:11 +0000 Subject: [PATCH 1/3] Initial plan From 76ad5e49df8bb1553390e0cf8deab8772c5f7723 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:05:44 +0000 Subject: [PATCH 2/3] Optimize performance: Touch effect removal, SearchBar CTS disposal, ListItem divider logic, SKLottie caching, Shell enumeration Co-authored-by: Vetle444 <35739538+Vetle444@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ src/library/DIPS.Mobile.UI/API/Library/DUI.cs | 9 +++++++++ .../Components/ListItems/ListItem.cs | 14 +++++++++++--- .../Components/Searching/SearchBar.cs | 1 + .../DIPS.Mobile.UI/Components/Shell/Shell.cs | 12 ++++++++++-- src/library/DIPS.Mobile.UI/Effects/Touch/Touch.cs | 7 +++++-- 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 566fc8355..51865bbdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [55.2.3] +- [Performance] Optimized Touch effect removal from O(n²) to O(n) in RemoveEffects. +- [Performance] Fixed CancellationTokenSource disposal leak in SearchBar on rapid text changes. +- [Performance] Replaced LINQ allocation in ListItem auto-divider logic with zero-allocation loop. +- [Performance] Cached SKLottieViewResources registration check to avoid repeated LINQ scans. +- [Performance] Reduced double enumeration in Shell memory diagnostics to single-pass loop. + ## [55.2.2] - [iOS26][Tip] Added more padding. diff --git a/src/library/DIPS.Mobile.UI/API/Library/DUI.cs b/src/library/DIPS.Mobile.UI/API/Library/DUI.cs index 250539061..e7a154a16 100644 --- a/src/library/DIPS.Mobile.UI/API/Library/DUI.cs +++ b/src/library/DIPS.Mobile.UI/API/Library/DUI.cs @@ -51,8 +51,15 @@ public static void RemoveViewsLocatedOnTopOfPage() RemovePlatformSpecificViewsLocatedOnTopOfPage(); } + private static bool s_skLottieResourcesAdded; + public static void EnsureSkLottieResourcesAdded() { + if (s_skLottieResourcesAdded) + { + return; + } + // try register with the current app var merged = Application.Current?.Resources?.MergedDictionaries; if (merged == null) @@ -64,6 +71,8 @@ public static void EnsureSkLottieResourcesAdded() { merged.Add(new SKLottieViewResources()); } + + s_skLottieResourcesAdded = true; } private static partial void RemovePlatformSpecificViewsLocatedOnTopOfPage(); diff --git a/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs b/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs index e18e47281..3c95d190e 100644 --- a/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs +++ b/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs @@ -278,9 +278,17 @@ private async void OnVerticalStackLayoutSizeChanged(object? sender, EventArgs e) if(!IsVisible) return; - if (m_verticalStackLayout!.Where(item => ((item as View)!).IsVisible) - .ToList() - .IndexOf(this) == 0) + var isFirst = true; + foreach (var item in m_verticalStackLayout!) + { + if (item is View { IsVisible: true } view) + { + isFirst = ReferenceEquals(view, this); + break; + } + } + + if (isFirst) { return; } diff --git a/src/library/DIPS.Mobile.UI/Components/Searching/SearchBar.cs b/src/library/DIPS.Mobile.UI/Components/Searching/SearchBar.cs index f645aa4bc..66807056f 100644 --- a/src/library/DIPS.Mobile.UI/Components/Searching/SearchBar.cs +++ b/src/library/DIPS.Mobile.UI/Components/Searching/SearchBar.cs @@ -42,6 +42,7 @@ private void OnUnloaded(object? sender, EventArgs e) private async void OnTextChanged(string newTextValue, string oldTextValue) { SearchCancellationToken?.Cancel(); //Cancel the previous search + SearchCancellationToken?.Dispose(); SearchCancellationToken = new CancellationTokenSource(); try diff --git a/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs b/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs index e6fc09fa6..ce57c6683 100644 --- a/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs +++ b/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs @@ -165,8 +165,16 @@ private static async Task TryResolvePoppedModalPages(List mo if (DUI.IsDebug) { - var alivePages = pageCollectionContentTargets.Where(t => t is not null && t.IsAlive).ToList(); - var garbageCollectedPages = pageCollectionContentTargets.Where(t => t is null || !t.IsAlive).ToList(); + List alivePages = []; + List garbageCollectedPages = []; + + foreach (var t in pageCollectionContentTargets) + { + if (t is not null && t.IsAlive) + alivePages.Add(t); + else + garbageCollectedPages.Add(t); + } var alivePageNames = string.Join(", ", alivePages.Select(p => p!.Name)); var gcPageNames = string.Join(", ", garbageCollectedPages.Select(p => p?.Name ?? "null")); diff --git a/src/library/DIPS.Mobile.UI/Effects/Touch/Touch.cs b/src/library/DIPS.Mobile.UI/Effects/Touch/Touch.cs index 6abe25a4c..948d16c81 100644 --- a/src/library/DIPS.Mobile.UI/Effects/Touch/Touch.cs +++ b/src/library/DIPS.Mobile.UI/Effects/Touch/Touch.cs @@ -94,9 +94,12 @@ private static void OnTouchPropertiesChanged(BindableObject bindable, object old private static void RemoveEffects(View view) { - while (view.Effects.Any(e => e is Touch)) + for (var i = view.Effects.Count - 1; i >= 0; i--) { - view.Effects.Remove(view.Effects.FirstOrDefault(e => e is Touch)); + if (view.Effects[i] is Touch) + { + view.Effects.RemoveAt(i); + } } } From 4a0278b1c3b821c5f5ae4d729f40bdabd66ed0d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:06:52 +0000 Subject: [PATCH 3/3] Fix isFirst initialization bug in ListItem divider logic Co-authored-by: Vetle444 <35739538+Vetle444@users.noreply.github.com> --- src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs b/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs index 3c95d190e..ebc66e036 100644 --- a/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs +++ b/src/library/DIPS.Mobile.UI/Components/ListItems/ListItem.cs @@ -278,7 +278,7 @@ private async void OnVerticalStackLayoutSizeChanged(object? sender, EventArgs e) if(!IsVisible) return; - var isFirst = true; + var isFirst = false; foreach (var item in m_verticalStackLayout!) { if (item is View { IsVisible: true } view)