Toast odpowiednik dla Xamarin Forms

89

Czy istnieje sposób na użycie Xamarin Forms (nie specyficznych dla systemu Android lub iOS), aby mieć wyskakujące okienko, takie jak Android robi z Toast, które nie wymaga interakcji użytkownika i znika po (krótkim) czasie?

Od przeszukiwania widzę tylko alerty, które wymagają kliknięcia przez użytkownika, aby zniknąć.

Jimmy
źródło

Odpowiedzi:

170

Jest na to proste rozwiązanie. Korzystając z DependencyService , możesz łatwo uzyskać podejście Toast-Like w systemie Android i iOS.

Utwórz interfejs w swoim wspólnym pakiecie.

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

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

sekcja iOS

W iOs nie ma natywnego rozwiązania takiego jak Toast, więc musimy wdrożyć własne podejście.

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

Należy pamiętać, że na każdej platformie musimy zarejestrować nasze klasy w DependencyService.

Teraz możesz uzyskać dostęp do usługi Toast w dowolnym miejscu naszego projektu.

DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);
Alex Chengalan
źródło
20
To zdecydowanie najlepsza odpowiedź na to pytanie. Nie są potrzebne żadne wtyczki ani biblioteki innych firm.
Bret Faller
4
w linii DependencyService otrzymuję komunikat „Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu”.
Joyce de Lanna
5
Tak, to jak dotąd najlepsza odpowiedź, lubię usługę dla osób uzależnionych
Lutaaya Huzaifah Idris
1
Pełna wygrana. Czy zdarzyło Ci się mieć również wersję UWP?
Nieminen,
1
@MengTim Wydaje się, że naprawiłem zablokowany interfejs użytkownika, tworząc za każdym razem nowy alert i licznik czasu. Obie należy przekazać DismissMessage.
Ian Warburton,
13

Oto wersja kodu iOS Alexa Chengalana, która pozwala uniknąć przyklejania się interfejsu użytkownika, gdy wyświetlanych jest wiele wiadomości ...

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
źródło
10

Możesz użyć pakietu Acr.UserDialogs z nuget i kodu jak poniżej,

Acr.UserDialogs.UserDialogs.Instance.Toast(Message, new TimeSpan(3));
KiShOrE
źródło
9

Zwykle używamy wtyczki Egors Toasts, ale ponieważ wymaga ona uprawnień na iOS dla bieżącego projektu, poszliśmy inną drogą za pomocą Rg.Plugins.Popup nuget ( https://github.com/rotorgames/Rg.Plugins.Popup ).

Napisałem podstawową stronę xaml / cs typu 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">
...

i mieć go utworzonego przez usługę, której interfejs rejestrujesz podczas uruchamiania aplikacji lub używasz Xamarin.Forms.DependencyService do pobierania usługi, również byłby opłacalny.

Usługa wysyła wiadomości na stronę pochodną PopupPage i robi

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

Strona Popup może zostać zamknięta przez użytkownika, dotykając poza wyświetlaną stroną (zakładając, że nie wypełniła ekranu).

Wydaje się, że działa to szczęśliwie na iOS / Droid, ale jestem otwarty na korektę, jeśli ktoś wie, co to jest ryzykowny sposób robienia tego.

Allister
źródło
rg pop-upy są świetne. Wykonuję podobne obejście, aby załadować wyświetlacz lub wskaźnik aktywności na całej stronie. Problem z innymi wtyczkami polega na tym, że są one zależne od funkcji asynchronicznych i głównego wątku, ale rg popup może działać w drugim wątku, co czyni go bardzo użytecznym. dobry pomysł, ale chciałbym, żeby natywny wyglądał jak na tostach androida.
Emil,
Jak dotąd jest to najlepsza metoda robienia toastów między platformami. Rg.Popup jest super elastyczny i używam go w prawie każdym projekcie. Nie ma potrzeby używania innych wtyczek ani kodu platformy do wyświetlania tostów,
GiampaoloGabba
8

Dodając do odpowiedzi Alexa, oto wariant 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();
  }
}

Kolorystyka i stylizacja zależy od Ciebie, w MaxHeightrzeczywistości jest to wymagane, aby utrzymać minimalną wysokość.

Gábor
źródło
Więc zarejestrowanie go jako usługi zależności nie wymaga platformy UWP?
Olorunfemi Ajibulu
Działa dokładnie tak samo, jak pozostałe dwa warianty. Tak, usługa zależności.
Gábor
7

Możesz użyć IUserDialog NuGet i po prostu użyć jego toastAlert

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

UserDialogs.Instance.Toast(toastConfig);
mohammad kamali
źródło
4

Oto fragment kodu, którego używam, aby pokazać toast w 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
źródło
4

Polecam Plugin.Toastbibliotekę od nuget. To dobrze działa.

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

lub z biblioteki ACR.UserDialogs Nuget

UserDialogs.Instance.ShowLoading("Loading");
Fk Bey
źródło
Czy jest sposób, aby przenieść go na szczyt? Dostosuj i pokaż wielokrotności?
G_Money,
Nie. ta biblioteka obsługuje tylko podstawowe wiadomości wyskakujące. możesz po prostu zmienić kolor tła i tekstu oraz czas trwania wiadomości.
Fk Bey,
4

@MengTim, aby naprawić problem z wieloma tostami w rozwiązaniu @ alex-chengalan, po prostu zawinąłem wszystko w środku ShowAlert()czekiem, aby sprawdzić, czy alerti alertDelaysą zerowe, a następnie w DismissMessageśrodku, anulowane alerti 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;
        }
    }

Wydawało się, że przynajmniej poprawiło to zawieszenie interfejsu użytkownika, jeśli szukasz szybkiego rozwiązania. Próbowałem wyświetlić toast po przejściu do nowej strony i wierzę, PresentViewControllerże ustawienie zasadniczo przerywa moją nawigację. Przepraszam, że nie komentowałem w wątku, moja reputacja jest zbyt niska :(

Gunnar
źródło
1

W Forms nie ma wbudowanego mechanizmu, ale ten pakiet NuGet dostarcza coś podobnego

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

Uwaga: to nie są tosty w stylu Androida, jak żądano w pytaniu, ale toasty w stylu UWP, które są powiadomieniami w całym systemie.

Jason
źródło
5
Android Toast oznacza zupełnie inną rzecz - to wyskakująca wiadomość. Ta biblioteka służy do powiadomień w całym systemie.
Vojtěch Sázel
Powinienem był przeczytać komentarz, zanim zainstalowałem bibliotekę, tylko po to, aby zauważyć, że nie są to tosty w stylu Androida. Proszę wyjaśnić to w odpowiedzi.
findusl
1

To jest moja ulepszona ShowAlertwersja wersji Iana Warburtona, która zapewnia wyświetlanie toastu nawet na wyskakującej stronie. Ponadto toast jest odrzucany, jeśli użytkownik kliknie poza toastem. Użyłem, UIAlertControllerStyle.ActionSheetże wygląda jak tosty, ale też działaUIAlertControllerStyle.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);
        });
    }

Mam nadzieję, że to komuś pomoże!

Pierre-Alexandre Flèche
źródło
1

Dostosowałem niestandardowe wyskakujące okienko za pomocą Rg.Plugins.Popup NuGet. Oto przykład:

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

następnie na swojej podstawowej stronie możesz dodać następujący kod, aby po chwili pokazać i ukryć „toast”:

public async void showpopup(string msg)
    {
        await Navigation.PushPopupAsync(new Toast(msg));
        await Task.Delay(3000);
        await Navigation.PopPopupAsync(true);   
    }
Patricia Lopes
źródło
0

Powyższe odpowiedzi na iOS działały dla mnie, ale z jednym małym problemem - ostrzeżenie: próba zaprezentowania UIAlertController ... którego widok nie znajduje się w hierarchii okien!

Po kilku poszukiwaniach natknąłem się na tę niepowiązaną odpowiedź, która pomogła. Plakat skomentował: „To wygląda głupio, ale działa”, co jest słuszne w obu przypadkach.

Tak więc zmodyfikowałem powyższą funkcję ShowAlert () za pomocą tych linii, które wydają się działać:

    var rootVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
    while ( rootVC.PresentedViewController != null) {
        rootVC = rootVC.PresentedViewController;
    }
    rootVC.PresentViewController( alert, true, null);
bobwki
źródło
Dang - poniżej widzę jeszcze lepszą wersję tego autorstwa @ Pierre-Alexandre Flèche. Jak to wcześniej przegapiłem?
bobwki
0

W przypadku 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
źródło
-5

Możesz użyć DisplayAlert("", "", "", "" );

O.Ahmadpoor
źródło
1
To wcale nie reaguje jak toast, potrzebuje działania, aby kontynuować.
CennoxX