Xamarin Forms Android:页面弹出过渡时保持被弹出页面可见
解决Xamarin Forms PopAsync时动画页面提前隐藏的问题
这个问题我之前在做Xamarin Forms自定义导航动画的时候也踩过坑!核心原因就是默认的Navigation.PopAsync()逻辑会在动画启动前就把当前要弹出的页面从视觉树里移除,导致动画过程中没有背景页面,看起来像是从空白飞入。下面是我摸索出来的解决办法,分Android和iOS两端来说:
Android端解决方案
在自定义的NavigationRenderer里,重写OnPopViewAsync方法,手动控制动画流程,先保留当前页面的可见性,等动画完成后再执行默认的页面弹出逻辑:
using Android.Animation; using Android.Views; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationRenderer))] namespace YourAppName.Droid.Renderers { public class CustomNavigationRenderer : NavigationRenderer { protected override Task<bool> OnPopViewAsync(Page page, bool animated) { if (animated) { // 获取当前要弹出页面的原生Android视图 var currentNativeView = (page as VisualElement).GetRenderer().View; // 获取要回到的目标页面的原生视图 var targetNativeView = (Element as NavigationPage).CurrentPage.GetRenderer().View; // 确保当前页面在动画期间保持可见 currentNativeView.Visibility = ViewStates.Visible; // 创建自定义动画:当前页面滑出屏幕右侧,目标页面从左侧滑入 var exitAnim = ObjectAnimator.OfFloat(currentNativeView, "translationX", 0, Context.Resources.DisplayMetrics.WidthPixels); var enterAnim = ObjectAnimator.OfFloat(targetNativeView, "translationX", -Context.Resources.DisplayMetrics.WidthPixels, 0); var animSet = new AnimatorSet(); animSet.PlayTogether(exitAnim, enterAnim); animSet.SetDuration(300); // 动画时长,和你的自定义动画保持一致 var taskCompletionSource = new TaskCompletionSource<bool>(); animSet.AddListener(new AnimatorListenerAdapter { public override void OnAnimationEnd(Animator animation) { // 动画结束后,再执行默认的Pop逻辑(关闭动画避免重复执行) base.OnPopViewAsync(page, false).ContinueWith(t => taskCompletionSource.SetResult(t.Result)); // 清理动画资源,防止内存泄漏 currentNativeView.ClearAnimation(); targetNativeView.ClearAnimation(); } }); animSet.Start(); return taskCompletionSource.Task; } // 非动画场景直接调用默认逻辑 return base.OnPopViewAsync(page, animated); } } }
iOS端解决方案
iOS端需要重写UINavigationController的PopViewController方法,同样先保留当前页面的视图,等动画完成后再清理:
using CoreAnimation; using Foundation; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationRenderer))] namespace YourAppName.iOS.Renderers { public class CustomNavigationRenderer : NavigationRenderer { public override UIViewController PopViewController(bool animated) { if (animated) { var currentVC = TopViewController; var previousVC = ViewControllers[ViewControllers.Length - 2]; // 创建自定义转场动画 var transition = new CATransition(); transition.Duration = 0.3; transition.Type = CATransitionType.Push; transition.Subtype = CATransitionSubtype.FromLeft; // 从左侧推入,对应Pop的反向动画 // 给视图层添加动画 View.Layer.AddAnimation(transition, CATransition.AnimationKey); // 先关闭动画执行默认Pop,避免系统自动处理视图隐藏 var poppedVC = base.PopViewController(false); // 动画期间把当前VC的视图加到目标VC上,保持可见 currentVC.View.RemoveFromSuperview(); previousVC.View.AddSubview(currentVC.View); // 动画结束后清理视图 DispatchQueue.MainQueue.DispatchAfter(new DispatchTime(DispatchTime.Now, new TimeSpan(0, 0, 0, 0, 300)), () => { currentVC.View.RemoveFromSuperview(); currentVC.Dispose(); // 释放资源 }); return poppedVC; } // 非动画场景直接调用默认逻辑 return base.PopViewController(animated); } } }
关键注意事项
- 内存泄漏防范:动画结束后一定要清理视图并释放资源,尤其是iOS端要手动Dispose不再需要的视图控制器。
- 动画时长统一:自定义动画的时长要和你设置的导航动画时长保持一致,避免出现视觉错位。
- 适配不同屏幕:Android端用
DisplayMetrics.WidthPixels获取屏幕宽度,确保动画在不同设备上都正常。 - 冲突排查:如果你的项目还有其他导航相关的自定义逻辑,要确保和这个渲染器的逻辑不冲突。
内容的提问来源于stack exchange,提问作者Mitchell
相关产品推荐
相关产品推荐

