Equivalente do brinde para Xamarin Forms

90

Existe alguma maneira de usar o Xamarin Forms (não específico para Android ou iOS) de ter um pop-up, como o Android faz com o Toast, que não precisa de interação do usuário e desaparece após um (curto) período de tempo?

Ao pesquisar ao redor, tudo o que vejo são alertas que precisam de cliques do usuário para desaparecer.

Jimmy
fonte

Respostas:

171

Existe uma solução simples para isso. Ao usar o DependencyService, você pode obter facilmente a abordagem Toast-Like no Android e no iOS.

Crie uma interface em seu pacote comum.

public interface IMessage
{
    void LongAlert(string message);
    void ShortAlert(string message);
}

Seção Android

[assembly: Xamarin.Forms.Dependency(typeof(MessageAndroid))]
namespace Your.Namespace
{
    public class MessageAndroid : IMessage
    {
        public void LongAlert(string message)
        {
            Toast.MakeText(Application.Context, message, ToastLength.Long).Show();
        }

        public void ShortAlert(string message)
        {
            Toast.MakeText(Application.Context, message, ToastLength.Short).Show();
        }
    }
}

seção iOS

Em iOs não há solução nativa como o Toast, então precisamos implementar nossa própria abordagem.

[assembly: Xamarin.Forms.Dependency(typeof(MessageIOS))]
namespace Bahwan.iOS
{
    public class MessageIOS : IMessage
    {
        const double LONG_DELAY = 3.5;
        const double SHORT_DELAY = 2.0;

        NSTimer alertDelay;
        UIAlertController alert;

        public void LongAlert(string message)
        {
            ShowAlert(message, LONG_DELAY);
        }
        public void ShortAlert(string message)
        {
            ShowAlert(message, SHORT_DELAY);
        }

        void ShowAlert(string message, double seconds)
        {
            alertDelay = NSTimer.CreateScheduledTimer(seconds, (obj) =>
            {
                dismissMessage();
            });
            alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
        }

        void dismissMessage()
        {
            if (alert != null)
            {
                alert.DismissViewController(true, null);
            }
            if (alertDelay != null)
            {
                alertDelay.Dispose();
            }
        }
    }
}

Observe que em cada plataforma, temos que registrar nossas classes com DependencyService.

Agora você pode acessar o serviço Toast em qualquer lugar do nosso projeto.

DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);
Alex Chengalan
fonte
20
Esta é de longe a melhor resposta a esta pergunta. Não são necessários plug-ins ou bibliotecas de terceiros.
Bret Faller de
4
na linha DependencyService estou recebendo "Referência de objeto não definida para uma instância de um objeto."
Joyce de Lanna
5
Sim, esta é a melhor resposta até agora, eu gosto do serviço de dependência
Lutaaya Huzaifah Idris
1
Cheio de vitória. Você teria uma versão UWP disso também?
Nieminen
1
@MengTim Parece que consertei a IU travada criando um novo alerta e cronômetro a cada vez. Ambos precisam ser passados DismissMessage.
Ian Warburton
14

Esta é uma versão do código iOS de Alex Chengalan que evita que a IU grude quando várias mensagens são mostradas ...

public class MessageIOS : IMessage
    {
        const double LONG_DELAY = 3.5;
        const double SHORT_DELAY = 0.75;

        public void LongAlert(string message)
        {
            ShowAlert(message, LONG_DELAY);
        }

        public void ShortAlert(string message)
        {
            ShowAlert(message, SHORT_DELAY);
        }

        void ShowAlert(string message, double seconds)
        {
            var alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);

            var alertDelay = NSTimer.CreateScheduledTimer(seconds, obj =>
            {
                DismissMessage(alert, obj);
            });

            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
        }

        void DismissMessage(UIAlertController alert, NSTimer alertDelay)
        {
            if (alert != null)
            {
                alert.DismissViewController(true, null);
            }

            if (alertDelay != null)
            {
                alertDelay.Dispose();
            }
        }
    }
Ian Warburton
fonte
11

Você pode usar o pacote Acr.UserDialogs do nuget e o código abaixo,

Acr.UserDialogs.UserDialogs.Instance.Toast(Message, new TimeSpan(3));
KiShOrE
fonte
9

Normalmente usaríamos o plugin Egors Toasts, mas como ele requer permissões no iOS para um projeto atual, seguimos um caminho diferente usando o nuget Rg.Plugins.Popup ( https://github.com/rotorgames/Rg.Plugins.Popup )

Eu escrevi uma página xaml / cs básica do tipo PopupPage,

<?xml version="1.0" encoding="utf-8" ?>
<popup:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:popup="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
         x:Class="YourApp.Controls.ToastPage">
...

e tê-lo criado por um serviço, cuja interface você registra no início do aplicativo ou usa o Xamarin.Forms.DependencyService para buscar o serviço também seria viável.

O serviço atualiza a página derivada do PopupPage e

await PopupNavigation.PushAsync(newToastPage);
await Task.Delay(2000);
await PopupNavigation.PopAllAsync();

A página Popup pode ser descartada pelo usuário tocando fora da exibição da página (assumindo que não preencheu a tela).

Isso parece funcionar bem no iOS / Droid, mas estou aberto a correções se alguém souber o que é uma maneira arriscada de fazer isso.

Allister
fonte
pop-ups rg são ótimos. Eu faço uma solução alternativa semelhante para carregar a exibição ou o indicador de atividade na página inteira. O problema com outros plug-ins é que eles dependem de funções assíncronas e do thread principal, mas o rg popup pode ser executado no segundo thread, o que o torna muito útil. Boa ideia, de fato, mas eu gostaria de ter uma aparência nativa no Android.
Emil
Até agora, este é o melhor método para fazer o brinde entre plataformas. Rg.Popup é super flexibile e eu o uso em quase todos os projetos. Não há necessidade de usar outros plug-ins ou código de plataforma para exibir toasts,
GiampaoloGabba
8

Somando-se à resposta de Alex, aqui está a variante UWP:

public class Message : IMessage {
  private const double LONG_DELAY = 3.5;
  private const double SHORT_DELAY = 2.0;

  public void LongAlert(string message) =>
    ShowMessage(message, LONG_DELAY);

  public void ShortAlert(string message) =>
    ShowMessage(message, SHORT_DELAY);

  private void ShowMessage(string message, double duration) {
    var label = new TextBlock {
      Text = message,
      Foreground = new SolidColorBrush(Windows.UI.Colors.White),
      HorizontalAlignment = HorizontalAlignment.Center,
      VerticalAlignment = VerticalAlignment.Center,
    };
    var style = new Style { TargetType = typeof(FlyoutPresenter) };
    style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.Black)));
    style.Setters.Add(new Setter(FrameworkElement.MaxHeightProperty, 1));
    var flyout = new Flyout {
      Content = label,
      Placement = FlyoutPlacementMode.Full,
      FlyoutPresenterStyle = style,
    };

    flyout.ShowAt(Window.Current.Content as FrameworkElement);

    var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(duration) };
    timer.Tick += (sender, e) => {
      timer.Stop();
      flyout.Hide();
    };
    timer.Start();
  }
}

Colorir e estilizar é com você, MaxHeighté realmente necessário para manter a altura no mínimo.

Gábor
fonte
Portanto, registrá-lo como um serviço de dependência não precisa para o UWP?
Olorunfemi Ajibulu
Funciona exatamente como as outras duas variantes. Sim, um serviço de dependência.
Gábor
7

Você pode usar IUserDialog NuGet e simplesmente usar seu toastAlert

var toastConfig = new ToastConfig("Toasting...");
toastConfig.SetDuration(3000);
toastConfig.SetBackgroundColor(System.Drawing.Color.FromArgb(12, 131, 193));

UserDialogs.Instance.Toast(toastConfig);
Mohammad Kamali
fonte
4

Aqui está um trecho de código que estou usando para mostrar a torrada no Xamarin.iOS

  public void ShowToast(String message, UIView view)
    {
        UIView residualView = view.ViewWithTag(1989);
        if (residualView != null)
            residualView.RemoveFromSuperview();

        var viewBack = new UIView(new CoreGraphics.CGRect(83, 0, 300, 100));
        viewBack.BackgroundColor = UIColor.Black;
        viewBack.Tag = 1989;
        UILabel lblMsg = new UILabel(new CoreGraphics.CGRect(0, 20, 300, 60));
        lblMsg.Lines = 2;
        lblMsg.Text = message;
        lblMsg.TextColor = UIColor.White;
        lblMsg.TextAlignment = UITextAlignment.Center;
        viewBack.Center = view.Center;
        viewBack.AddSubview(lblMsg);
        view.AddSubview(viewBack);
        roundtheCorner(viewBack);
        UIView.BeginAnimations("Toast");
        UIView.SetAnimationDuration(3.0f);
        viewBack.Alpha = 0.0f;
        UIView.CommitAnimations();
    }
Durai Amuthan.H
fonte
4

Eu recomendaria a Plugin.Toastbiblioteca de nuget. Isso funciona bem.

CrossToastPopUp.Current.ShowToastMessage("my toast message");

ou da biblioteca ACR.UserDialogs Nuget

UserDialogs.Instance.ShowLoading("Loading");
Fk Bey
fonte
Existe uma maneira de movê-lo para o topo? Personalizar e mostrar múltiplos?
G_Money
não. esta biblioteca suporta apenas mensagens básicas do sistema. você apenas pode alterar a cor do bg e do texto e a duração da mensagem.
Fk Bey
4

@MengTim, para corrigir o problema de vários toast na solução de @ alex-chengalan, simplesmente envolvi tudo dentro de ShowAlert()uma verificação para ver se alerte alertDelaysão nulos, então dentro DismissMessage, anulados alerte alertDelay.

void ShowAlert(string message, double seconds)
    {
        if(alert == null && alertDelay == null) {
            alertDelay = NSTimer.CreateScheduledTimer(seconds, (obj) =>
            {
                DismissMessage();
            });
            alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
        }
    }

    void DismissMessage()
    {
        if (alert != null)
        {
            alert.DismissViewController(true, null);
            alert = null;
        }
        if (alertDelay != null)
        {
            alertDelay.Dispose();
            alertDelay = null;
        }
    }

Isso pareceu pelo menos esclarecer o travamento da IU, se você está procurando uma solução rápida. Eu estava tentando exibir o brinde na navegação para uma nova página e acredito que a PresentViewControllerconfiguração estava basicamente cancelando minha navegação. Desculpe, não comentei no tópico, minha reputação é muito baixa :(

Gunnar
fonte
2

Esta é minha ShowAlertversão melhorada da versão de Ian Warburton para garantir que o brinde seja exibido mesmo na página pop-up. Além disso, a torrada é dispensada se o usuário clicar fora dela. Eu usei UIAlertControllerStyle.ActionSheetaquele visual como torradas, mas também funciona comUIAlertControllerStyle.Alert

    void ShowAlert(string message, double seconds)
    {
        var alert = UIAlertController.Create(null, message, UIAlertControllerStyle.ActionSheet);

        var alertDelay = NSTimer.CreateScheduledTimer(seconds, obj =>
        {
            DismissMessage(alert, obj);
        });

        var viewController = UIApplication.SharedApplication.KeyWindow.RootViewController;
        while (viewController.PresentedViewController != null)
        {
            viewController = viewController.PresentedViewController;
        }
        viewController.PresentViewController(alert, true, () =>
        {
            UITapGestureRecognizer tapGesture = new UITapGestureRecognizer(_ => DismissMessage(alert, null));
            alert.View.Superview?.Subviews[0].AddGestureRecognizer(tapGesture);
        });
    }

Espero que isso ajude alguém!

Pierre-Alexandre Flèche
fonte
1

Não há nenhum mecanismo embutido no Formulários, mas este pacote nuget fornece algo semelhante

https://github.com/EgorBo/Toasts.Forms.Plugin

Nota: Estes não são toasts no estilo Android, conforme solicitado na pergunta, mas no estilo UWP, que são notificações de todo o sistema.

Jason
fonte
5
Android Toast significa algo completamente diferente - é uma mensagem pop-up. Esta biblioteca é para notificações de todo o sistema.
Vojtěch Sázel
Deveria ter lido o comentário antes de instalar a biblioteca apenas para perceber que esses não são brindes do estilo android. Por favor, deixe isso claro na resposta.
findusl
1

Personalizei um pop-up personalizado com Rg.Plugins.Popup NuGet, este é um exemplo:

 <pages:PopupPage.Animation>
    <animations:ScaleAnimation 
        PositionIn="Center"
        PositionOut="Center"
        ScaleIn="1.2"
        ScaleOut="0.8"
        DurationIn="600"
        DurationOut="600"
        EasingIn="Linear"
       EasingOut="Linear"/>
</pages:PopupPage.Animation>

<Frame CornerRadius="10"  
    HeightRequest="30"
       VerticalOptions="End"
       HorizontalOptions="Fill"
       HasShadow="False"
        Padding="0" Margin="40,50"
       OutlineColor="LightGray">
    <StackLayout 
    Opacity="0.4"
       BackgroundColor="White">
    <Label
        x:Name="lbl"
        LineBreakMode="WordWrap"
        HorizontalTextAlignment="Center"
                    VerticalTextAlignment="Center"

        VerticalOptions="CenterAndExpand"
        HorizontalOptions="Center" TextColor="Black" FontSize="12">
                <Label.FontFamily>
                    <OnPlatform x:TypeArguments="x:String">
                        <On Platform="iOS" Value="NewJuneMedium" />
                    </OnPlatform>
                </Label.FontFamily>
            </Label>
</StackLayout>
    </Frame>

em seguida, em sua página de conteúdo base, você pode adicionar o seguinte código, para mostrar e ocultar o "brinde" depois de um tempo:

public async void showpopup(string msg)
    {
        await Navigation.PushPopupAsync(new Toast(msg));
        await Task.Delay(3000);
        await Navigation.PopPopupAsync(true);   
    }
Patricia Lopes
fonte
0

As respostas do iOS acima funcionaram para mim, mas para um pequeno problema - um aviso: Tente apresentar UIAlertController ... cuja visualização não está na hierarquia da janela!

Depois de alguma pesquisa, encontrei esta resposta não relacionada que ajudou. O autor da postagem comentou "Isso parece estúpido, mas funciona", o que está certo em ambos os casos.

Portanto, modifiquei a função ShowAlert () acima com estas linhas, que parecem funcionar:

    var rootVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
    while ( rootVC.PresentedViewController != null) {
        rootVC = rootVC.PresentedViewController;
    }
    rootVC.PresentViewController( alert, true, null);
bobwki
fonte
Dang - Vejo uma versão ainda melhor disso abaixo de @Pierre-Alexandre Flèche. Como eu perdi isso antes?
bobwki
0

Para UWP

public void ShowMessageFast(string message)
    {
        ToastNotifier ToastNotifier = ToastNotificationManager.CreateToastNotifier();
        Windows.Data.Xml.Dom.XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
        Windows.Data.Xml.Dom.XmlNodeList toastNodeList = toastXml.GetElementsByTagName("text");
        toastNodeList.Item(0).AppendChild(toastXml.CreateTextNode("Test"));
        toastNodeList.Item(1).AppendChild(toastXml.CreateTextNode(message));
        Windows.Data.Xml.Dom.IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
        Windows.Data.Xml.Dom.XmlElement audio = toastXml.CreateElement("audio");
        audio.SetAttribute("src", "ms-winsoundevent:Notification.SMS");

        ToastNotification toast = new ToastNotification(toastXml);
        toast.ExpirationTime = DateTime.Now.AddSeconds(4);
        ToastNotifier.Show(toast);
    }
Fabien Richard
fonte
-5

Você pode usar DisplayAlert("", "", "", "" );

O.Ahmadpoor
fonte
1
Isso não reage de forma alguma como um brinde, é necessária uma ação para continuar.
CennoxX