Toast équivalent pour Xamarin Forms

89

Existe-t-il un moyen d'utiliser Xamarin Forms (pas spécifique à Android ou iOS) pour créer une fenêtre contextuelle, comme Android le fait avec Toast, qui ne nécessite aucune interaction de l'utilisateur et disparaît après une (courte) période de temps?

À partir de la recherche, tout ce que je vois sont des alertes qui nécessitent des clics d'utilisateurs pour disparaître.

Jimmy
la source

Réponses:

170

Il existe une solution simple pour cela. En utilisant DependencyService, vous pouvez facilement obtenir l'approche Toast-Like dans Android et iOS.

Créez une interface dans votre package commun.

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

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

section iOS

Dans iOs, il n'y a pas de solution native comme Toast, nous devons donc implémenter notre propre approche.

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

Veuillez noter que dans chaque plateforme, nous devons enregistrer nos classes avec DependencyService.

Vous pouvez désormais accéder au service Toast partout dans notre projet.

DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);
Alex Chengalan
la source
20
C'est de loin la meilleure réponse à cette question. Aucun plug-in ou bibliothèque tiers requis.
Bret Faller
4
dans la ligne DependencyService, j'obtiens "La référence d'objet n'est pas définie sur une instance d'un objet."
Joyce de Lanna
5
Ouais, c'est la meilleure réponse à ce jour, j'aime le service de dépendance
Lutaaya Huzaifah Idris
1
Plein de victoire. En auriez-vous également une version UWP?
Nieminen
1
@MengTim Je semble avoir corrigé l'interface utilisateur bloquée en créant une nouvelle alerte et une nouvelle minuterie à chaque fois. Les deux doivent être transmis DismissMessage.
Ian Warburton
13

Voici une version du code iOS d'Alex Chengalan qui évite que l'interface utilisateur ne colle lorsque plusieurs messages sont affichés ...

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
la source
10

Vous pouvez utiliser le package Acr.UserDialogs à partir de nuget et du code comme ci-dessous,

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

Nous utiliserions normalement le plugin Egors Toasts, mais comme il nécessite des autorisations sur iOS pour un projet actuel, nous avons emprunté une voie différente en utilisant Rg.Plugins.Popup nuget ( https://github.com/rotorgames/Rg.Plugins.Popup ).

J'ai écrit une page de base xaml / cs de type 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">
...

et faites-le créer par un service, dont vous inscrivez l'interface au démarrage de l'application ou utilisez Xamarin.Forms.DependencyService pour récupérer le service serait également viable.

Le service actualise la page dérivée de PopupPage, et fait

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

La page Popup peut être rejetée par l'utilisateur en appuyant en dehors de l'affichage de la page (en supposant qu'elle n'a pas rempli l'écran).

Cela semble fonctionner correctement sur iOS / Droid, mais je suis ouvert à la correction si quelqu'un sait ce que c'est une façon risquée de le faire.

Allister
la source
Les popups rg sont super. Je fais une solution de contournement similaire pour charger l'affichage ou l'indicateur d'activité en pleine page. problème avec les autres plugins sont-ils dépendants des fonctions async et du thread principal, mais rg popup peut fonctionner sur le 2ème thread, ce qui le rend très utile. bonne idée en effet mais je souhaite avoir un look natif comme sur les toasts Android.
Emil
Jusqu'à présent, c'est la meilleure méthode pour faire des toasts multiplateformes. Rg.Popup est super flexible, et je l'utilise dans presque tous les projets. Pas besoin d'utiliser d'autres plugins ou code de plate-forme pour afficher des toasts,
GiampaoloGabba
8

Pour ajouter à la réponse d'Alex, voici la 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();
  }
}

La coloration et le coiffage dépendent de vous, il MaxHeightest en fait nécessaire de garder la hauteur au minimum.

Gábor
la source
Donc, l'enregistrer en tant que service de dépendance n'a pas besoin de l'UWP?
Olorunfemi Ajibulu
Cela fonctionne exactement comme les deux autres variantes. Oui, un service de dépendance.
Gábor
7

Vous pouvez utiliser IUserDialog NuGet et simplement utiliser son toastAlert

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

UserDialogs.Instance.Toast(toastConfig);
mohammad kamali
la source
4

Voici un extrait de code que j'utilise pour afficher le toast dans 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
la source
4

Je recommanderais la Plugin.Toastbibliothèque de nuget. Ça marche bien.

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

ou de la bibliothèque ACR.UserDialogs Nuget

UserDialogs.Instance.ShowLoading("Loading");
Fk Bey
la source
Y a-t-il un moyen de le déplacer vers le haut? Personnaliser et afficher les multiples?
G_Money
non. cette bibliothèque ne prend en charge que les messages toast de base. vous pouvez simplement changer la couleur du bg et du texte et la durée du message.
Fk Bey
4

@MengTim, pour résoudre le problème de toast multiple dans la solution de @ alex-chengalan, j'ai simplement tout enveloppé à l'intérieur ShowAlert()avec une vérification pour voir si alertet alertDelaysont nuls, puis à l'intérieur DismissMessage, annulés alertet 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;
        }
    }

Cela semblait au moins effacer le blocage de l'interface utilisateur, si vous recherchez une solution rapide. J'essayais d'afficher le toast lors de la navigation vers une nouvelle page, et je crois que le PresentViewControllerréglage annulait essentiellement ma navigation. Désolé je n'ai pas commenté dans le fil, ma réputation est trop basse :(

Gunnar
la source
1

Il n'y a pas de mécanisme intégré dans Forms, mais ce package nuget fournit quelque chose de similaire

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

Remarque: ce ne sont pas des toasts de style Android comme demandé dans la question, mais des toasts de style UWP qui sont des notifications à l'échelle du système.

Jason
la source
5
Android Toast signifie une chose complètement différente - c'est un message contextuel. Cette bibliothèque est destinée aux notifications à l'échelle du système.
Vojtěch Sázel
J'aurais dû lire le commentaire avant d'installer la bibliothèque juste pour remarquer que ce ne sont pas des toasts de style androïde. Veuillez préciser cela dans la réponse.
findusl
1

Ceci est ma ShowAlertversion améliorée de la version d'Ian Warburton pour s'assurer que le toast est affiché même sur la page contextuelle. En outre, le toast est rejeté si l'utilisateur clique en dehors du toast. J'ai utilisé UIAlertControllerStyle.ActionSheetce look aime le pain grillé mais cela fonctionne aussi avecUIAlertControllerStyle.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);
        });
    }

J'espère que cela aidera quelqu'un!

Pierre-Alexandre Flèche
la source
1

J'ai personnalisé un popup personnalisé avec Rg.Plugins.Popup NuGet voici un exemple:

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

puis dans votre page de contenu de base, vous pouvez ajouter le code suivant, pour afficher et masquer le "toast" après un certain temps:

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

Les réponses iOS ci-dessus ont fonctionné pour moi, mais pour un petit problème - un avertissement: essayez de présenter UIAlertController ... dont la vue n'est pas dans la hiérarchie des fenêtres!

Après quelques recherches, je suis tombé sur cette réponse sans rapport qui a aidé. L'affiche a commenté "Cela a l'air stupide mais ça marche", ce qui est juste sur les deux points.

Donc, j'ai modifié la fonction ShowAlert () ci-dessus avec ces lignes, qui semblent fonctionner:

    var rootVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
    while ( rootVC.PresentedViewController != null) {
        rootVC = rootVC.PresentedViewController;
    }
    rootVC.PresentViewController( alert, true, null);
bobwki
la source
Dang - Je vois une version encore meilleure de ceci ci-dessous de @ Pierre-Alexandre Flèche. Comment l'ai-je manqué avant?
bobwki
0

Pour 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
la source
-5

Vous pouvez utiliser DisplayAlert("", "", "", "" );

O.Ahmadpoor
la source
1
Cela ne réagit pas du tout comme un toast, il faut une action pour continuer.
CennoxX