Toast equivalente para Xamarin Forms

89

¿Hay alguna forma de usar Xamarin Forms (no específico de Android o iOS) para tener una ventana emergente, como lo hace Android con Toast, que no necesita interacción del usuario y desaparece después de un período (corto) de tiempo?

Al buscar, todo lo que veo son alertas que necesitan que el usuario haga clic para desaparecer.

Palanqueta
fuente

Respuestas:

170

Existe una solución sencilla para esto. Al usar DependencyService , puede obtener fácilmente el enfoque Toast-Like tanto en Android como en iOS.

Cree una interfaz en su paquete común.

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

Sección de 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();
        }
    }
}

sección de iOS

En iOs no existe una solución nativa como Toast, por lo que debemos implementar nuestro propio enfoque.

[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();
            }
        }
    }
}

Tenga en cuenta que en cada plataforma, tenemos que registrar nuestras clases con DependencyService.

Ahora puede acceder al servicio Toast en cualquier lugar de nuestro proyecto.

DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);
Alex Chengalan
fuente
20
Esta es, con mucho, la mejor respuesta a esta pregunta. No se necesitan complementos o bibliotecas de terceros.
Bret Faller
4
en la línea DependencyService obtengo "Referencia de objeto no configurada en una instancia de un objeto".
Joyce de Lanna
5
Sí, esta es la mejor respuesta hasta ahora, me gusta el servicio de dependencia
Lutaaya Huzaifah Idris
1
Lleno de victoria. ¿También tendrías una versión para UWP de esto?
Nieminen
1
@MengTim Parece que he solucionado la interfaz de usuario atascada creando una nueva alerta y temporizador cada vez. Ambos deben pasarse a DismissMessage.
Ian Warburton
13

Aquí hay una versión del código iOS de Alex Chengalan que evita que la interfaz de usuario se quede pegada cuando se muestran varios mensajes ...

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
fuente
10

Puede usar el paquete Acr.UserDialogs de nuget y el código como se muestra a continuación,

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

Normalmente usamos el complemento Egors Toasts, pero como requiere permisos en iOS para un proyecto actual, hemos tomado una ruta diferente usando Rg.Plugins.Popup nuget ( https://github.com/rotorgames/Rg.Plugins.Popup ).

Escribí una página básica xaml / cs de 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">
...

y hacer que lo cree un servicio, cuya interfaz registre al iniciar la aplicación o use Xamarin.Forms.DependencyService para buscar el servicio también sería viable.

El servicio se actualiza en la página derivada de PopupPage y no

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

El usuario puede cerrar la página emergente tocando fuera de la pantalla de la página (suponiendo que no haya llenado la pantalla).

Esto parece funcionar felizmente en iOS / Droid, pero estoy abierto a la corrección si alguien sabe que es una forma arriesgada de hacerlo.

Allister
fuente
las ventanas emergentes rg son geniales. Hago una solución similar para cargar la pantalla o el indicador de actividad en la página completa. El problema con otros complementos es que dependen de funciones asíncronas y del hilo principal, pero la ventana emergente rg puede ejecutarse en el segundo hilo, lo que lo hace muy útil. De hecho, es una buena idea, pero deseo que los nativos se vean como en las tostadas de Android.
Emil
Hasta ahora, este es el mejor método para hacer un brindis multiplataforma. Rg.Popup es súper flexible y lo uso en casi todos los proyectos. No es necesario usar otros complementos o código de plataforma para mostrar brindis,
GiampaoloGabba
8

Agregando a la respuesta de Alex, aquí está la variante de 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();
  }
}

El color y el estilo dependen de usted, en MaxHeightrealidad se requiere mantener la altura al mínimo.

Gábor
fuente
Entonces, ¿registrarlo como servicio de dependencia no es necesario para la UWP?
Olorunfemi Ajibulu
Funciona exactamente como las otras dos variantes. Sí, un servicio de dependencia.
Gábor
7

Puede usar IUserDialog NuGet y simplemente usar su toastAlert

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

UserDialogs.Instance.Toast(toastConfig);
mohammad kamali
fuente
4

Aquí hay un fragmento de código que estoy usando para mostrar el brindis en 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
fuente
4

Recomendaría la Plugin.Toastbiblioteca de nuget. Funciona bien.

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

o de ACR.UserDialogs Nuget libriary

UserDialogs.Instance.ShowLoading("Loading");
Fk Bey
fuente
¿Hay alguna forma de moverlo a la cima? ¿Personalizar y mostrar múltiplos?
G_Money
No. esta biblioteca solo admite mensajes tostados básicos. solo puede cambiar bg y el color del texto y la duración del mensaje.
Fk Bey
4

@MengTim, para solucionar el problema de las tostadas múltiples en la solución de @ alex-chengalan, simplemente envolví todo dentro ShowAlert()con una verificación para ver si alerty alertDelayson nulos, luego dentro DismissMessage, anulados alerty 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;
        }
    }

Eso pareció al menos aclarar el bloqueo de la interfaz de usuario, si está buscando una solución rápida. Estaba tratando de mostrar el brindis en la navegación a una nueva página, y creo que la PresentViewControllerconfiguración estaba esencialmente cancelando mi navegación. Lo siento, no comenté en el hilo, mi reputación es demasiado baja :(

Gunnar
fuente
1

No hay un mecanismo incorporado en Forms, pero este paquete nuget proporciona algo similar

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

Nota: Estos no son brindis al estilo de Android como se solicita en la pregunta, sino al estilo de UWP que son notificaciones de todo el sistema.

Jason
fuente
5
Android Toast significa algo completamente diferente: es un mensaje emergente. Esta biblioteca es para notificaciones de todo el sistema.
Vojtěch Sázel
Debería haber leído el comentario antes de instalar la biblioteca solo para notar que estos no son brindis al estilo de Android. Por favor, aclare eso en la respuesta.
findusl
1

Esta es mi ShowAlertversión mejorada de la versión de Ian Warburton para garantizar que el brindis se muestre incluso en la página emergente. Además, el brindis se descarta si el usuario hace clic fuera del brindis. Usé UIAlertControllerStyle.ActionSheetese look como tostadas pero también funciona conUIAlertControllerStyle.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 esto ayude a alguien !

Pierre-Alexandre Flèche
fuente
1

Personalicé una ventana emergente personalizada con Rg.Plugins.Popup NuGet, este es un ejemplo:

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

luego en su página de contenido base puede agregar el siguiente código, para mostrar y ocultar el "brindis" después de un tiempo:

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

Las respuestas de iOS anteriores funcionaron para mí, pero por un pequeño problema: una advertencia: ¡Intente presentar UIAlertController ... cuya vista no está en la jerarquía de ventanas!

Después de una búsqueda, encontré esta respuesta no relacionada que ayudó. El cartel comentó "Esto parece estúpido pero funciona", lo cual es correcto en ambos aspectos.

Entonces, modifiqué la función ShowAlert () anterior con estas líneas, que parecen funcionar:

    var rootVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
    while ( rootVC.PresentedViewController != null) {
        rootVC = rootVC.PresentedViewController;
    }
    rootVC.PresentViewController( alert, true, null);
bobwki
fuente
Dang, veo una versión aún mejor de esto a continuación de @ Pierre-Alexandre Flèche. ¿Cómo me lo perdí 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
fuente
-5

Puedes usar DisplayAlert("", "", "", "" );

Oh Ahmadpoor
fuente
1
Esto no reacciona en absoluto como un brindis, necesita una acción para continuar.
CennoxX