如何访问 DependencyService 中的 ViewController 以呈现 MFMailComposeViewController?我尝试使用 Application.Context 但这似乎只适用于 Android。有什么建议吗?
5 回答
您可以通过执行window.RootController.PresentViewController (mail controller, true, null);
. 根据您的应用架构,RootViewController 可能不是层次结构中可用的 ViewController。在这种情况下,您会得到一个
警告:尝试在 <Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x14fd1530> 上呈现 <MFMailComposeViewController: 0x16302c30>,其视图不在窗口层次结构中!
在这种情况下,您必须挖掘具体的 ViewController,在我的情况下是:
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
这有点邪恶,但可以工作(已提交此问题以供将来修复)。
完整的解决方案如下所示:
在你的AppDelegate.cs
,添加这个:
public UIWindow Window {
get { return window; }
}
在您的 PCL 项目中,声明接口:ISendMailService.cs
public interface ISendMailService
{
void ComposeMail (string[] recipients, string subject, string messagebody = null, Action<bool> completed = null);
}
在您的 iOS 项目中,实现并注册接口:SendMailService.cs
[assembly: DependencyAttribute(typeof(SendMailService))]
public class SendMailService : ISendMailService
{
public void ComposeMail (string[] recipients, string subject, string messagebody = null, Action<bool> completed = null)
{
var controller = new MFMailComposeViewController ();
controller.SetToRecipients (recipients);
controller.SetSubject (subject);
if (!string.IsNullOrEmpty (messagebody))
controller.SetMessageBody (messagebody, false);
controller.Finished += (object sender, MFComposeResultEventArgs e) => {
if (completed != null)
completed (e.Result == MFMailComposeResult.Sent);
e.Controller.DismissViewController (true, null);
};
//Adapt this to your app structure
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);
}
}
现在,您可以从 Xamarin.Forms PCL 项目中使用它:
new Button {
Font = Font.SystemFontOfSize (NamedSize.Medium),
Text = "Contact us",
TextColor = Color.White,
BackgroundColor = ColorsAndStyles.LightBlue,
BorderRadius = 0,
Command = new Command (()=>{
var mailservice = DependencyService.Get<ISendMailService> ();
if (mailservice == null)
return;
mailservice.ComposeMail (new [] {"foo@example.com"}, "Test", "Hello, World");
})
}
采用:UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
我想添加一个基于 KeyWindow 并不总是主窗口的附加答案。(当您在用户与操作表或警报对话框交互后呈现控制器时会发生这种情况)
public static UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
throw new InvalidOperationException("There's no current active window");
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
这将保证您获得 Xamarin Forms 平台呈现器窗口,然后找到最前面呈现的 ViewController 并将其返回以用于呈现您需要呈现的任何 UI 或视图控制器。
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
这仅适用于所有解决方案
仅供参考。我花了一些时间才弄清楚如何从模式窗口启动它。
解决方案来了:
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.PresentedViewController;
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);