diff --git a/CHANGELOG.md b/CHANGELOG.md index 17a95bd2c..5a6640b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [55.6.4] +- Fixed multiple memory leaks across components: unsubscribed events in `BottomSheetHandler`, `BaseDatePickerHandler`, `BaseNullableDatePicker`, `TabView`, `Shell`, `SkeletonView`, `SegmentedControl`, `StateView`, `ItemPicker`, `SearchPage`, `ScrollPickerHandler`, `FloatingNavigationButton`, `GalleryBottomSheet`, and `SystemMessage` + ## [55.6.3] - [ScrollView][iOS] ShouldBounce now sets correct native property. diff --git a/build/bootstrapper/bootstrapper.sh b/build/bootstrapper/bootstrapper.sh index 5904d7d8e..de0272971 100755 --- a/build/bootstrapper/bootstrapper.sh +++ b/build/bootstrapper/bootstrapper.sh @@ -60,7 +60,7 @@ fi #azure-CLI -if az > /dev/null ; then +if az > /dev/null 2>&1 ; then echo "✅ Azure CLI was found" else echo "❌ Azure CLI not found, installing it..." diff --git a/src/library/DIPS.Mobile.UI/API/Camera/Gallery/BottomSheet/GalleryBottomSheet.cs b/src/library/DIPS.Mobile.UI/API/Camera/Gallery/BottomSheet/GalleryBottomSheet.cs index 0b4d23631..39ae85d4a 100644 --- a/src/library/DIPS.Mobile.UI/API/Camera/Gallery/BottomSheet/GalleryBottomSheet.cs +++ b/src/library/DIPS.Mobile.UI/API/Camera/Gallery/BottomSheet/GalleryBottomSheet.cs @@ -144,18 +144,7 @@ private void GoToEditState() m_currentlyRotatedImageDisplayed.TranslationY -= m_topToolbar.HeightRequest; - m_currentlyRotatedImageDisplayed.SizeChanged += delegate - { - if (m_startingImageWidth is not null) - return; - - m_startingImageWidth = m_currentlyRotatedImageDisplayed.Width; - m_startingImageHeight = m_currentlyRotatedImageDisplayed.Height; - - m_carouselView.Opacity = 0; - m_navigatePreviousImageButton.IsVisible = false; - m_navigateNextImageButton.IsVisible = false; - }; + m_currentlyRotatedImageDisplayed.SizeChanged += OnRotatedImageSizeChanged; m_rotatingImageTcs = null; @@ -169,11 +158,25 @@ private void GoToDefaultState() m_positionBeforeEdit = m_carouselView!.Position; + m_currentlyRotatedImageDisplayed.SizeChanged -= OnRotatedImageSizeChanged; m_grid.Remove(m_currentlyRotatedImageDisplayed); UpdateNavigationButtonsVisibility(m_carouselView.Position); OnImagesChanged(); } + private void OnRotatedImageSizeChanged(object? sender, EventArgs e) + { + if (m_startingImageWidth is not null) + return; + + m_startingImageWidth = m_currentlyRotatedImageDisplayed.Width; + m_startingImageHeight = m_currentlyRotatedImageDisplayed.Height; + + m_carouselView!.Opacity = 0; + m_navigatePreviousImageButton.IsVisible = false; + m_navigateNextImageButton.IsVisible = false; + } + async void IGalleryDefaultStateObserver.RemoveImage() { if(Images.Count == 0) @@ -276,6 +279,7 @@ private void OnImagesChanged() if (m_carouselView is not null) { + m_carouselView.SizeChanged -= OnCarouselViewSizeChanged; m_carouselView.PositionChanged -= CarouselViewOnPositionChanged; try { @@ -310,30 +314,32 @@ private void OnImagesChanged() }; m_carouselViewWrapperView.Content = m_carouselView; - m_carouselView.SizeChanged += delegate - { - if (m_hasSetToolbarHeights) - { - return; - } - - var actualImageHeight = m_carouselView.Width / CameraPreview.ThreeFourRatio; - var letterBoxHeight = m_carouselView.Height - actualImageHeight; - - m_topToolbar.HeightRequest = letterBoxHeight * CameraPreview.TopToolbarPercentHeightOfLetterBox; - m_bottomToolbar.HeightRequest = letterBoxHeight * CameraPreview.BottomToolbarPercentHeightOfLetterBox; - - m_carouselViewWrapperView.TranslationY -= m_topToolbar.HeightRequest; - m_navigatePreviousImageButton.TranslationY -= m_topToolbar.HeightRequest; - m_navigateNextImageButton.TranslationY -= m_topToolbar.HeightRequest; - - m_hasSetToolbarHeights = true; - }; + m_carouselView.SizeChanged += OnCarouselViewSizeChanged; m_carouselView.PositionChanged += CarouselViewOnPositionChanged; m_grid.Insert(0, m_carouselViewWrapperView); } + private void OnCarouselViewSizeChanged(object? sender, EventArgs e) + { + if (m_hasSetToolbarHeights) + { + return; + } + + var actualImageHeight = m_carouselView!.Width / CameraPreview.ThreeFourRatio; + var letterBoxHeight = m_carouselView.Height - actualImageHeight; + + m_topToolbar.HeightRequest = letterBoxHeight * CameraPreview.TopToolbarPercentHeightOfLetterBox; + m_bottomToolbar.HeightRequest = letterBoxHeight * CameraPreview.BottomToolbarPercentHeightOfLetterBox; + + m_carouselViewWrapperView.TranslationY -= m_topToolbar.HeightRequest; + m_navigatePreviousImageButton.TranslationY -= m_topToolbar.HeightRequest; + m_navigateNextImageButton.TranslationY -= m_topToolbar.HeightRequest; + + m_hasSetToolbarHeights = true; + } + private void OnStartingIndexChanged() { if(m_carouselView is not null) diff --git a/src/library/DIPS.Mobile.UI/API/Camera/Shared/iOS/CameraSession.cs b/src/library/DIPS.Mobile.UI/API/Camera/Shared/iOS/CameraSession.cs index 5e663dbd2..ef573f33a 100644 --- a/src/library/DIPS.Mobile.UI/API/Camera/Shared/iOS/CameraSession.cs +++ b/src/library/DIPS.Mobile.UI/API/Camera/Shared/iOS/CameraSession.cs @@ -78,6 +78,7 @@ await Task.Run(() => if (PreviewView is not null) { PreviewView.OnZoomChanged -= PreviewViewOnZoomChanged; + PreviewView.OnTapToFocus -= PreviewViewOnOnTapToFocus; PreviewView?.Dispose(); PreviewView = null; } diff --git a/src/library/DIPS.Mobile.UI/Components/Alerting/SystemMessage/SystemMessage.cs b/src/library/DIPS.Mobile.UI/Components/Alerting/SystemMessage/SystemMessage.cs index 9b4e8ade2..2b3841968 100644 --- a/src/library/DIPS.Mobile.UI/Components/Alerting/SystemMessage/SystemMessage.cs +++ b/src/library/DIPS.Mobile.UI/Components/Alerting/SystemMessage/SystemMessage.cs @@ -125,5 +125,6 @@ public void Dispose() { m_timer.Stop(); m_timer.Elapsed -= OnTimerEnded; + m_timer.Dispose(); } } \ No newline at end of file diff --git a/src/library/DIPS.Mobile.UI/Components/BottomSheets/Android/BottomSheetHandler.cs b/src/library/DIPS.Mobile.UI/Components/BottomSheets/Android/BottomSheetHandler.cs index 3e5d0ab41..7d658e845 100644 --- a/src/library/DIPS.Mobile.UI/Components/BottomSheets/Android/BottomSheetHandler.cs +++ b/src/library/DIPS.Mobile.UI/Components/BottomSheets/Android/BottomSheetHandler.cs @@ -39,6 +39,8 @@ public partial class BottomSheetHandler : ContentViewHandler private BottomSheetHeader m_bottomSheetHeader; private List> m_weakSearchBars = []; private WeakReference? m_weakCurrentFocusedSearchBar; + private BottomSheetCallback? m_bottomSheetCallback; + private KeyListener? m_keyListener; public AView OnBeforeOpening(IMauiContext mauiContext, Context context, AView bottomSheetAndroidView, RelativeLayout rootLayout, LinearLayout bottomSheetLayout) @@ -46,10 +48,11 @@ public AView OnBeforeOpening(IMauiContext mauiContext, Context context, AView bo if (VirtualView is not BottomSheet bottomSheet) return new AView(Context); m_bottomSheet = bottomSheet; - bottomSheet.BottomSheetDialog.Behavior.AddBottomSheetCallback( - new BottomSheetCallback(this)); + m_bottomSheetCallback = new BottomSheetCallback(this); + m_keyListener = new KeyListener(this); + bottomSheet.BottomSheetDialog.Behavior.AddBottomSheetCallback(m_bottomSheetCallback); bottomSheet.BottomSheetDialog.SetOnShowListener(new DialogInterfaceOnShowListener(this)); - bottomSheet.BottomSheetDialog.SetOnKeyListener(new KeyListener(this)); + bottomSheet.BottomSheetDialog.SetOnKeyListener(m_keyListener); //Add a handle, with a innerGrid that works as a big hit box for the user to hit //Inspired by com.google.android.material.bottomsheet.BottomSheetDragHandleView , which will be added in Xamarin Android Material Design v1.7.0. https://github.com/material-components/material-components-android/commit/ac7b761294808748df167b50b223b591ca9dac06 @@ -234,6 +237,16 @@ protected override void DisconnectHandler(ContentViewGroup platformView) { base.DisconnectHandler(platformView); + if (m_bottomSheetCallback is not null) + { + m_bottomSheet.BottomSheetDialog.Behavior.RemoveBottomSheetCallback(m_bottomSheetCallback); + m_bottomSheetCallback = null; + } + + m_bottomSheet.BottomSheetDialog.SetOnShowListener(null); + m_bottomSheet.BottomSheetDialog.SetOnKeyListener(null); + m_keyListener = null; + s_mEmptyNonFitToContentView?.RemoveFromParent(); m_bottomSheetHeader.DisconnectHandlers(); diff --git a/src/library/DIPS.Mobile.UI/Components/Loading/Skeleton/SkeletonView.cs b/src/library/DIPS.Mobile.UI/Components/Loading/Skeleton/SkeletonView.cs index e458ff066..26449101a 100644 --- a/src/library/DIPS.Mobile.UI/Components/Loading/Skeleton/SkeletonView.cs +++ b/src/library/DIPS.Mobile.UI/Components/Loading/Skeleton/SkeletonView.cs @@ -134,6 +134,16 @@ private BoxView CreateBox(SkeletonShape shape) return box; } + protected override void OnHandlerChanging(HandlerChangingEventArgs args) + { + base.OnHandlerChanging(args); + + if (args.NewHandler is not null) + return; + + StopAnimation(); + } + private void StartAnimation() { StopAnimation(); diff --git a/src/library/DIPS.Mobile.UI/Components/Loading/StateView/StateView.cs b/src/library/DIPS.Mobile.UI/Components/Loading/StateView/StateView.cs index 94151038c..e252fef42 100644 --- a/src/library/DIPS.Mobile.UI/Components/Loading/StateView/StateView.cs +++ b/src/library/DIPS.Mobile.UI/Components/Loading/StateView/StateView.cs @@ -120,11 +120,20 @@ private async Task FadeIn(IView? view) } } + private StateViewModel? m_previousStateViewModel; + private void OnStateViewModelChanged() { if(StateViewModel is null) return; + if (m_previousStateViewModel is not null) + { + m_previousStateViewModel.OnStateChanged -= OnStateChanged; + } + + m_previousStateViewModel = StateViewModel; + ErrorView ??= new ErrorView { BindingContext = StateViewModel.Error }; LoadingView ??= new LoadingView { BindingContext = StateViewModel.Loading }; EmptyView ??= new EmptyView { BindingContext = StateViewModel.Empty }; diff --git a/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/Android/FloatingNavigationButtonHandler.cs b/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/Android/FloatingNavigationButtonHandler.cs index 69c6b1608..d4965ec3a 100644 --- a/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/Android/FloatingNavigationButtonHandler.cs +++ b/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/Android/FloatingNavigationButtonHandler.cs @@ -11,17 +11,24 @@ private static partial void MapIsClickable(FloatingNavigationButtonHandler handl if (handler.PlatformView is not global::Android.Views.View aView) return; if (handler.VirtualView is not FloatingNavigationButton fab) return; + aView.Click -= handler.OnNativeViewClick; + if (floatingNavigationButton.IsClickable) { aView.Clickable = true; - aView.Click += (_, _) => - { - _ = fab.Close(); - }; + aView.Click += handler.OnNativeViewClick; } else { aView.Clickable = false; } } + + private void OnNativeViewClick(object? sender, EventArgs e) + { + if (VirtualView is FloatingNavigationButton fab) + { + _ = fab.Close(); + } + } } \ No newline at end of file diff --git a/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/FloatingNavigationButtonService.cs b/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/FloatingNavigationButtonService.cs index 7214e5270..93921361e 100644 --- a/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/FloatingNavigationButtonService.cs +++ b/src/library/DIPS.Mobile.UI/Components/Navigation/FloatingNavigationButton/FloatingNavigationButtonService.cs @@ -150,6 +150,7 @@ public static void Close() public static void Remove() { PlatformRemove(); + FloatingNavigationButton = null; } private static partial void PlatformRemove(); diff --git a/src/library/DIPS.Mobile.UI/Components/Pickers/DatePickerShared/iOS/BaseDatePickerHandler.cs b/src/library/DIPS.Mobile.UI/Components/Pickers/DatePickerShared/iOS/BaseDatePickerHandler.cs index a3b000b3d..93f610fee 100644 --- a/src/library/DIPS.Mobile.UI/Components/Pickers/DatePickerShared/iOS/BaseDatePickerHandler.cs +++ b/src/library/DIPS.Mobile.UI/Components/Pickers/DatePickerShared/iOS/BaseDatePickerHandler.cs @@ -63,6 +63,7 @@ protected override void DisconnectHandler(UIDatePicker platformView) { base.DisconnectHandler(platformView); + platformView.ValueChanged -= OnValueChanged; platformView.EditingDidBegin -= OnOpen; platformView.EditingDidEnd -= OnClose; diff --git a/src/library/DIPS.Mobile.UI/Components/Pickers/ItemPicker/ItemPicker.cs b/src/library/DIPS.Mobile.UI/Components/Pickers/ItemPicker/ItemPicker.cs index 1786bd7be..3941535cf 100644 --- a/src/library/DIPS.Mobile.UI/Components/Pickers/ItemPicker/ItemPicker.cs +++ b/src/library/DIPS.Mobile.UI/Components/Pickers/ItemPicker/ItemPicker.cs @@ -154,6 +154,11 @@ private static void ItemsSourceChanged(BindableObject bindable, object oldValue, picker.AddContextMenuItems(); } + if (oldValue is INotifyCollectionChanged oldNotifyCollectionChanged) + { + oldNotifyCollectionChanged.CollectionChanged -= picker.OnCollectionChanged; + } + if (newValue is INotifyCollectionChanged notifyCollectionChanged) { notifyCollectionChanged.CollectionChanged += picker.OnCollectionChanged; diff --git a/src/library/DIPS.Mobile.UI/Components/Pickers/NullableDatePickerShared/BaseNullableDatePicker.cs b/src/library/DIPS.Mobile.UI/Components/Pickers/NullableDatePickerShared/BaseNullableDatePicker.cs index 8a41d76c2..e0845afed 100644 --- a/src/library/DIPS.Mobile.UI/Components/Pickers/NullableDatePickerShared/BaseNullableDatePicker.cs +++ b/src/library/DIPS.Mobile.UI/Components/Pickers/NullableDatePickerShared/BaseNullableDatePicker.cs @@ -27,7 +27,10 @@ protected override void OnHandlerChanging(HandlerChangingEventArgs args) base.OnHandlerChanging(args); if(args.NewHandler is null) + { + DateEnabledSwitch.Toggled -= OnSwitchToggled; return; + } m_dateOrTimePicker = CreateDateOrTimePicker(); m_dateOrTimePicker.IsVisible = false; diff --git a/src/library/DIPS.Mobile.UI/Components/Pickers/ScrollPicker/Android/ScrollPickerHandler.cs b/src/library/DIPS.Mobile.UI/Components/Pickers/ScrollPicker/Android/ScrollPickerHandler.cs index 740beecc8..227d87947 100644 --- a/src/library/DIPS.Mobile.UI/Components/Pickers/ScrollPicker/Android/ScrollPickerHandler.cs +++ b/src/library/DIPS.Mobile.UI/Components/Pickers/ScrollPicker/Android/ScrollPickerHandler.cs @@ -72,6 +72,7 @@ protected override void DisconnectHandler(Chip platformView) platformView.Click -= PlatformViewOnClick; m_scrollPickerViewModel.OnAnyComponentsDataInvalidated -= SetChipTitle; + m_scrollPickerViewModel.Dispose(); } } diff --git a/src/library/DIPS.Mobile.UI/Components/Pickers/SegmentedControl/SegmentedControl.cs b/src/library/DIPS.Mobile.UI/Components/Pickers/SegmentedControl/SegmentedControl.cs index 863dd5c93..133e12d87 100644 --- a/src/library/DIPS.Mobile.UI/Components/Pickers/SegmentedControl/SegmentedControl.cs +++ b/src/library/DIPS.Mobile.UI/Components/Pickers/SegmentedControl/SegmentedControl.cs @@ -95,32 +95,34 @@ private View CreateSegment() horizontalStackLayout.Add(checkedImage); horizontalStackLayout.Add(label); border.Content = horizontalStackLayout; - border.SizeChanged += ((sender, _) => - { - if (sender is not View view) return; + border.SizeChanged += OnBorderSizeChanged; + + return border; + } - // Sometimes on Android, the different does not have equal heights, so this is a workaround to ensure all borders have same height. + private void OnBorderSizeChanged(object? sender, EventArgs _) + { + if (sender is not Border border) return; + + // Sometimes on Android, the different does not have equal heights, so this is a workaround to ensure all borders have same height. #if __ANDROID__ border.HeightRequest = this.Height; #endif - if (view.BindingContext is not SelectableItemViewModel selectableListItem) return; - - var radius = Sizes.GetSize(SizeName.radius_xlarge); - var roundRectangle = new RoundRectangle() { StrokeThickness = 0}; - if (m_allSelectableItems.Last() == selectableListItem) - { - roundRectangle.CornerRadius = new CornerRadius(0, radius, 0, radius); - } - else if (m_allSelectableItems.First() == selectableListItem) - { - roundRectangle.CornerRadius = new CornerRadius(radius, 0, radius, 0); - } + if (border.BindingContext is not SelectableItemViewModel selectableListItem) return; - border.StrokeShape = roundRectangle; - }); + var radius = Sizes.GetSize(SizeName.radius_xlarge); + var roundRectangle = new RoundRectangle() { StrokeThickness = 0}; + if (m_allSelectableItems.Last() == selectableListItem) + { + roundRectangle.CornerRadius = new CornerRadius(0, radius, 0, radius); + } + else if (m_allSelectableItems.First() == selectableListItem) + { + roundRectangle.CornerRadius = new CornerRadius(radius, 0, radius, 0); + } - return border; + border.StrokeShape = roundRectangle; } private void OnItemTouched(SelectableItemViewModel selectableItemViewModel) @@ -158,9 +160,23 @@ private void SendDidSelect(object item) SelectedItemCommand?.Execute(item); } + private void UnsubscribeBorderEvents() + { + foreach (var child in m_horizontalStackLayout.Children) + { + if (child is Border border) + { + border.SizeChanged -= OnBorderSizeChanged; + } + } + } + private void ItemsSourceChanged() { if (ItemsSource == null) return; + + UnsubscribeBorderEvents(); + var listOfSelectableItems = new List(); foreach (var item in ItemsSource.Cast().ToList()) @@ -183,4 +199,14 @@ private void ItemsSourceChanged() m_allSelectableItems = listOfSelectableItems; BindableLayout.SetItemsSource(m_horizontalStackLayout, m_allSelectableItems); } + + protected override void OnHandlerChanging(HandlerChangingEventArgs args) + { + base.OnHandlerChanging(args); + + if (args.NewHandler is null) + { + UnsubscribeBorderEvents(); + } + } } \ No newline at end of file diff --git a/src/library/DIPS.Mobile.UI/Components/Searching/SearchPage.cs b/src/library/DIPS.Mobile.UI/Components/Searching/SearchPage.cs index 5b9717ee7..c799059b5 100644 --- a/src/library/DIPS.Mobile.UI/Components/Searching/SearchPage.cs +++ b/src/library/DIPS.Mobile.UI/Components/Searching/SearchPage.cs @@ -46,8 +46,6 @@ public SearchPage() SearchBar.SetBinding(SearchBar.DelayProperty, static (SearchPage searchPage) => searchPage.Delay, source: this); SearchBar.SetBinding(SearchBar.IsAutocorrectEnabledProperty, static (SearchPage searchPage) => searchPage.IsAutocorrectEnabled, source: this); - SearchBar.TextChanged += SearchBarOnTextChanged; - SearchBar.SearchCommand = new Command(() => OnSearchQueryChanged(SearchBar.Text)); SearchBar.ClearTextCommand = new Command(TextWasClearedFromClick); SearchBar.CancelCommand = CancelCommand; @@ -125,6 +123,7 @@ protected override void OnHandlerChanged() SearchBar.Focus(); } + SearchBar.TextChanged += SearchBarOnTextChanged; SearchBar.Focused += OnSearchBarFocused; } @@ -209,7 +208,6 @@ private async void OnSearchQueryChanged(string searchQuery) protected override void OnDisappearing() { - SearchBar.TextChanged -= SearchBarOnTextChanged; SearchBar.Unfocus(); base.OnDisappearing(); } @@ -279,6 +277,7 @@ protected override void OnHandlerChanging(HandlerChangingEventArgs args) if (args.NewHandler is null) { + SearchBar.TextChanged -= SearchBarOnTextChanged; m_resultCollectionView.Scrolled -= OnCollectionViewScrolled; SearchBar.Focused -= OnSearchBarFocused; } diff --git a/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs b/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs index 26dea9cea..5433de96e 100644 --- a/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs +++ b/src/library/DIPS.Mobile.UI/Components/Shell/Shell.cs @@ -35,6 +35,16 @@ public Shell() SetNavBarHasShadow(this, false); } + protected override void OnHandlerChanging(HandlerChangingEventArgs args) + { + base.OnHandlerChanging(args); + + if (args.NewHandler is not null) + return; + + Navigated -= OnNavigated; + } + private async void OnNavigated(object? sender, ShellNavigatedEventArgs e) { switch (e.Source) diff --git a/src/library/DIPS.Mobile.UI/Components/Slidable/SlidableLayout.cs b/src/library/DIPS.Mobile.UI/Components/Slidable/SlidableLayout.cs index 176708c4d..7ae8f6955 100644 --- a/src/library/DIPS.Mobile.UI/Components/Slidable/SlidableLayout.cs +++ b/src/library/DIPS.Mobile.UI/Components/Slidable/SlidableLayout.cs @@ -8,6 +8,7 @@ namespace DIPS.Mobile.UI.Components.Slidable public abstract partial class SlidableLayout : ContentView { private readonly PanGestureRecognizer m_panGestureRecognizer; + private readonly TapGestureRecognizer m_tapGestureRecognizer; private readonly AccelerationService m_accelerator = new(true); private int m_lastId = -2; // Different than default of SlideProperties private double m_startSlideLocation; @@ -37,9 +38,9 @@ public SlidableLayout() m_panGestureRecognizer.PanUpdated += PanGestureRecognizerPanUpdated; - var tapGestureRecognizer = new TapGestureRecognizer(); - GestureRecognizers.Add(tapGestureRecognizer); - tapGestureRecognizer.Tapped += OnEntireLayoutTapped; + m_tapGestureRecognizer = new TapGestureRecognizer(); + GestureRecognizers.Add(m_tapGestureRecognizer); + m_tapGestureRecognizer.Tapped += OnEntireLayoutTapped; m_currentOrientation = DeviceDisplay.MainDisplayInfo.Orientation; } @@ -296,6 +297,7 @@ protected override void OnHandlerChanging(HandlerChangingEventArgs args) if (args.NewHandler is null) { m_panGestureRecognizer.PanUpdated -= PanGestureRecognizerPanUpdated; + m_tapGestureRecognizer.Tapped -= OnEntireLayoutTapped; } } } diff --git a/src/library/DIPS.Mobile.UI/Components/TabView/iOS/TabView.cs b/src/library/DIPS.Mobile.UI/Components/TabView/iOS/TabView.cs index 963150cb9..2cd5d1bbc 100644 --- a/src/library/DIPS.Mobile.UI/Components/TabView/iOS/TabView.cs +++ b/src/library/DIPS.Mobile.UI/Components/TabView/iOS/TabView.cs @@ -44,10 +44,7 @@ private void ItemsSourceChanged() list.ForEach(obj => { var tab = new Tab() { Title = obj.Title, Counter = obj.Counter }; - tab.Tapped += (sender, args) => - { - _ = TabToggled(tab); - }; + tab.Tapped += OnTabTapped; var item = tab; @@ -105,8 +102,19 @@ private void SetTextStyleForAllTabs() } } + private void OnTabTapped(object? sender, EventArgs args) + { + if (sender is Tab tab) + _ = TabToggled(tab); + } + private void ClearItems() { + foreach (var tab in m_tabItems) + { + tab.Tapped -= OnTabTapped; + } + m_tabItems.Clear(); m_stackLayout.Clear(); m_previouslySelectedTabIndex = -1;