Access ViewController в DependencyService представляет MFMailComposeViewController

Как я могу получить доступ к ViewController в моей службе DependencyService для представления MFMailComposeViewController? Я пробовал использовать Application.Context, но похоже, что он работает только на Android. Любые советы?

Ответ 1

Вы можете представить MFMailComposeViewController, выполнив window.RootController.PresentViewController (mail controller, true, null);. В зависимости от архитектуры вашего приложения RootViewController может не использоваться в ViewController в иерархии. В этом случае вы получите

Предупреждение: попытка представить < MFMailComposeViewController: 0x16302c30 > on < Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x14fd1530 > чей вид не находится в иерархии окон!

В этом случае вам нужно копать конкретный 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 [] {"[email protected]"}, "Test", "Hello, World");
    })
}

Ответ 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 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 и верните его для использования, представляя любой пользовательский интерфейс или контроллер просмотра, который вам нужно представить.

Ответ 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);