在 iOS 上,NavigationRenderer 具有虚拟方法 OnPopViewAsync 和 OnPushAsync(类似 on Android):
protected override Task<bool> OnPopViewAsync(Page page, bool animated)
{
return base.OnPopViewAsync(page, animated);
}
protected override Task<bool> OnPushAsync(Page page, bool animated)
{
return base.OnPushAsync(page, animated);
}
他们使用两个参数调用相应的基本方法,即页面和是否为过渡设置动画。因此,您可以使用以下方法启用或禁用动画:
- 派生自定义导航页面。
- 添加“动画”属性。
- 为您的自定义导航页面派生自定义导航渲染器。
- 使用“Animated”属性覆盖调用其基方法的 pop 和 push 方法。
请注意,我还没有尝试过这种方法,因为它需要做很多工作。但是禁用所有导航页面上的动画确实可以这样工作。
编辑:我花了几个小时才真正为自己的项目实施解决方案。因此,我将分享更多细节。 (我在 Xamarin.Forms 1.2.3-pre4 上开发和测试。)
自定义导航页面
除了上面提到的Animated属性之外,我的导航页面重新实现了两个转换函数,并添加了一个可选参数animated,默认为true。这样我们就可以保留所有现有代码,只在需要的地方添加false。
此外,这两种方法都会在推送/弹出页面后休眠很短的时间(10 毫秒)。如果没有这种延迟,我们就会遇到连续调用的麻烦。
public class CustomNavigationPage: NavigationPage
{
public bool Animated { get; private set; }
public CustomNavigationPage(Page page) : base(page)
{
}
// Analysis disable once MethodOverloadWithOptionalParameter
public async Task PushAsync(Page page, bool animated = true)
{
Animated = animated;
await base.PushAsync(page);
await Task.Run(delegate {
Thread.Sleep(10);
});
}
// Analysis disable once MethodOverloadWithOptionalParameter
public async Task<Page> PopAsync(bool animated = true)
{
Animated = animated;
var task = await base.PopAsync();
await Task.Run(delegate {
Thread.Sleep(10);
});
return task;
}
}
自定义导航渲染器
我的自定义导航页面的渲染器会覆盖这两种转换方法并将Animated 属性传递给它们的基本方法。 (这种方式注入flag有点丑,但我找不到更好的解决方案。)
public class CustomNavigationRenderer: NavigationRenderer
{
protected override Task<bool> OnPopViewAsync(Page page, bool animated)
{
return base.OnPopViewAsync(page, (Element as CustomNavigationPage).Animated);
}
protected override Task<bool> OnPushAsync(Page page, bool animated)
{
return base.OnPushAsync(page, (Element as CustomNavigationPage).Animated);
}
}
这适用于 iOS。但在 Android 上几乎是一样的。
一个示例应用程序
为了演示连续推送和弹出页面的可能性,我编写了以下应用程序。
App 类只是创建了一个新的DemoPage 包装到CustomNavigationPage 中。请注意,此示例必须可公开访问此实例。
public static class App
{
public static CustomNavigationPage NavigationPage;
public static Page GetMainPage()
{
return NavigationPage = new CustomNavigationPage(new DemoPage("Root"));
}
}
演示页面包含许多按不同顺序推送和弹出页面的按钮。您可以为每次调用PushAsync 或PopAsync 添加或删除false 选项。
public class DemoPage: ContentPage
{
public DemoPage(string title)
{
Title = title;
Content = new StackLayout {
Children = {
new Button {
Text = "Push",
Command = new Command(o => App.NavigationPage.PushAsync(new DemoPage("Pushed"))),
},
new Button {
Text = "Pop",
Command = new Command(o => App.NavigationPage.PopAsync()),
},
new Button {
Text = "Push + Pop",
Command = new Command(async o => {
await App.NavigationPage.PushAsync(new DemoPage("Pushed (will pop immediately)"));
await App.NavigationPage.PopAsync();
}),
},
new Button {
Text = "Pop + Push",
Command = new Command(async o => {
await App.NavigationPage.PopAsync(false);
await App.NavigationPage.PushAsync(new DemoPage("Popped and pushed immediately"));
}),
},
new Button {
Text = "Push twice",
Command = new Command(async o => {
await App.NavigationPage.PushAsync(new DemoPage("Pushed (1/2)"), false);
await App.NavigationPage.PushAsync(new DemoPage("Pushed (2/2)"));
}),
},
new Button {
Text = "Pop twice",
Command = new Command(async o => {
await App.NavigationPage.PopAsync(false);
await App.NavigationPage.PopAsync();
}),
},
},
};
}
}
重要提示:我花了好几个小时调试才发现您需要使用NavigationPage(或派生类)的实例而不是ContentPage 的Navigation!否则立即调用两个或多个 pop 或 push 会导致奇怪的行为和崩溃。