【问题标题】:Access ViewController in DependencyService to present MFMailComposeViewController在 DependencyService 中访问 ViewController 以呈现 MFMailComposeViewController
【发布时间】:2014-07-30 22:46:14
【问题描述】:

如何访问 DependencyService 中的 ViewController 以呈现 MFMailComposeViewController?我尝试使用 Application.Context 但这似乎只适用于 Android。有什么建议吗?

【问题讨论】:

    标签: xamarin.ios xamarin xamarin.forms


    【解决方案1】:

    您可以通过 window.RootController.PresentViewController (mail controller, true, null); 来呈现 MFMailComposeViewController。根据您的应用架构,RootViewController 可能不是层次结构中可用的 ViewController。在这种情况下,你会得到一个

    警告:尝试在 上显示 ,其视图不在窗口层次结构中!

    在这种情况下,您必须挖掘具体的 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");
        })
    }
    

    【讨论】:

    • 您能建议如何适应应用程序结构吗?
    • @FrTerstappen:下断点,在调试器的帮助下找到“正确”的孩子
    • 我可以使用根视图控制器。这个可以吗?还是我必须找到具体的控制器?
    • @Stephane Delcroix:我使用上面的代码从 Forms 打开 ViewController。它可以工作,但是如何将导航栏添加到 ViewController?我想添加带有后退按钮的导航栏,然后单击后退应该导航到表单页面。怎么办?
    【解决方案2】:

    使用:UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);

    【讨论】:

    • 一行代码,效果很好!非常感谢
    【解决方案3】:

    我想添加一个基于 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 或视图控制器。

    【讨论】:

    • 谢谢谢谢谢谢!我花了 3 个小时才阅读您的评论。
    【解决方案4】:
    UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
    

    这仅适用于上述所有解决方案

    【讨论】:

      【解决方案5】:

      仅供参考。我花了一些时间才弄清楚如何从模式窗口启动它。

      解决办法来了:

      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);
      

      【讨论】:

      • 如果正在使用 Actionsheet 或警报对话框,这会在 iPad 上的 iOS 10 中中断
      猜你喜欢
      • 2021-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多