web-dev-qa-db-fra.com

Toast équivalent pour Xamarin Forms

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

En parcourant tout ce que je vois, ce sont des alertes qui nécessitent des clics d’utilisateur pour disparaître.

43
Jimmy

Il existe une solution simple pour cela. En utilisant DependencyService , vous pouvez facilement utiliser l’approche Toast-Like sous 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’existe pas de solution native comme Toast, nous devons donc mettre en œuvre 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 sur chaque plate-forme, nous devons enregistrer nos cours auprès de DependencyService.

Vous pouvez maintenant accéder au service Toast n'importe où dans notre projet.

DependencyService.Get<iMessage>().ShortAlert(string message); 
DependencyService.Get<iMessage>().LongAlert(string message);
92
Alex Chengalan

Voici une version de iOS code d'Alex Chengalan qui évite que l'interface utilisateur ne reste bloquée 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();
            }
        }
    }
5
Ian Warburton

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 couleur et le style sont à vous, la MaxHeightest réellement nécessaire pour maintenir la hauteur au minimum.

4
Gábor

Voici un extrait de code que j'utilise pour afficher le pain grillé 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();
    }
3
Durai Amuthan.H

@MengTim, pour résoudre le problème de plusieurs toasts dans la solution de @ alex-chengalan, j'ai tout simplement inséré tout dans ShowAlert () en vérifiant si alert et alertDelay sont nuls, puis dans DismissMessage, annulés par alert et 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 éclaircir le problème d'interface utilisateur, si vous cherchez une solution rapide. J'essayais d'afficher le pain grillé lors de la navigation vers une nouvelle page et je pensais que le PresentViewController en cours de configuration annulait essentiellement ma navigation. Désolé, je n'ai pas commenté dans le fil, ma réputation est trop basse :(

3
Gunnar

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

J'ai écrit une page xaml/cs basique 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 le faire créer par un service, dont l'interface vous enregistrez 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

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

La page contextuelle peut être fermée par l'utilisateur en appuyant en dehors de l'affichage de la page (en supposant que l'écran ne soit pas rempli).

Cela semble fonctionner avec bonheur sur iOS/Droid, mais je suis ouvert à la correction si quelqu'un sait en quoi c'est risqué.

3
Allister

Voici ma version ShowAlert améliorée de la version de Ian Warburton afin de garantir que le pain grillé est affiché même sur une page contextuelle. En outre, le pain grillé est refusé si l'utilisateur clique en dehors du pain grillé . J'ai utilisé UIAlertControllerStyle.ActionSheet qui ressemble à du pain grillé mais il fonctionne également avec UIAlertControllerStyle.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! 

1

Il n'y a pas de mécanisme intégré dans Forms, mais ce paquet de 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.

1
Jason

Vous pouvez utiliser IUserDialog Nuget et simplement utiliser c'est toastAlert

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

UserDialogs.Instance.Toast(toastConfig);
0
mohammad kamali

Les réponses iOS ci-dessus ont fonctionné pour moi mais pour un seul petit problème - un avertissement: essayez de présenter UIAlertController ... dont la vue ne se trouve 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 commente "C’est stupide, mais ça marche", ce qui est exact des deux côtés.

J'ai donc 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);
0
bobwki