Roti panggang yang setara untuk Formulir Xamarin


90

Apakah ada cara menggunakan Formulir Xamarin (bukan khusus Android atau iOS) untuk memiliki pop-up, seperti yang dilakukan Android dengan Toast, yang tidak memerlukan interaksi pengguna dan akan hilang setelah periode waktu (singkat)?

Dari pencarian di sekitar semua yang saya lihat adalah peringatan yang membutuhkan klik pengguna untuk pergi.

Jawaban:


171

Ada solusi sederhana untuk ini. Dengan menggunakan DependencyService, Anda bisa dengan mudah mendapatkan pendekatan Toast-Like di Android dan iOS.

Buat antarmuka dalam paket umum Anda.

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

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

Bagian iOS

Di iOs tidak ada solusi asli seperti Toast, jadi kami perlu menerapkan pendekatan kami sendiri.

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

Please note that in each platform, we have to register our classes with DependencyService.

Now you can access out Toast service in anywhere in our project.

DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);

20
This is by far the best answer to this question. No third party plugins or libraries needed.
Bret Faller

4
in DependencyService line I am getting "Object reference not set to an instance of an object."
Joyce de Lanna

5
Yeah this is the best answer so far , i like dependency service
Lutaaya Huzaifah Idris

1
Full of win. Would you happen to have a UWP version of this as well?
Nieminen

1
@MengTim I seem to have fixed the stuck UI by creating a new alert and timer every time. Both need to be passed to DismissMessage.
Ian Warburton

14

Here's a version of Alex Chengalan's iOS code that avoids the UI sticking when multiple messages are shown...

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

11

You can use Acr.UserDialogs Package from nuget and code like below,

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

9

We'd normally use Egors Toasts plugin, but as it requires permissions on iOS for a current project we've gone a different route using Rg.Plugins.Popup nuget (https://github.com/rotorgames/Rg.Plugins.Popup).

I wrote a basic xaml/cs page of 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">
...

and have it created by a service, whose interface you register at app start or use Xamarin.Forms.DependencyService to fetch the service would be viable too.

The service news up the PopupPage derived page, and does

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

The Popup page can be dismissed by the user by tapping outside the page display (assuming it hasn't filled the screen).

This seems to work happily on iOS/Droid, but I'm open to correction if anyone knows what this is a risky way of doing it.


rg popups are great. I do similar workaround for loading display or activity indicator on full page. problem with other plugins are they are dependent on async functions and main thread but rg popup can run on 2nd thread which makes it very useful. good idea indeed but I wish to have native look like on android toasts.
Emil

So far this is the best method to do cross platform toast. Rg.Popup is super flexibile, and i use it in almost every project. No need to use other plugins or platform code to display toasts,
GiampaoloGabba

8

Adding to Alex's answer, here's the UWP variant:

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

Coloring and styling is up to you, the MaxHeightis actually required to keep the height at the minimum.


So registering it as a Dependency Service does not need for the UWP?
Olorunfemi Ajibulu

It works exactly as the other two variants. Yes, a dependency service.
Gábor

7

You can use IUserDialog NuGet and simply use it's toastAlert

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

UserDialogs.Instance.Toast(toastConfig);

4

Here is a code snippet that I am using to show the toast in 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();
    }

4

I would recommend Plugin.Toast library from nuget. It works well.

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

or from ACR.UserDialogs Nuget libriary

UserDialogs.Instance.ShowLoading("Loading");

Is there a way to move it to the top? Customize and show multiples?
G_Money

no. this library supports only basic toast messages. you just can change bg and text color and the duration of message.
Fk Bey

4

@MengTim, to fix the multiple toast issue in @alex-chengalan's solution, I simply wrapped everything within ShowAlert() with a check to see if alert and alertDelay are null, then within DismissMessage, nulled out alert and 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;
        }
    }

That seemed to at least clear up the UI hang, if you are looking for a quick fix. I was trying to display the toast on navigation to a new page, and believe that the PresentViewController being set was essentially cancelling out my navigation. Sorry I did not comment within the thread, my reputation is too low :(


2

This is my improved ShowAlert version of Ian Warburton's version to ensure that the toast is displayed even on popup page. Furthermore, the toast is dissmissed if the user click outside the toast. I used UIAlertControllerStyle.ActionSheet that look likes toast but it also work with 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);
        });
    }

I hope this will help someone !


1

There is no built-in mechanism in Forms, but this nuget package supplies something similar

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

Note: These are not Android style toasts as requested in the question but UWP style toasts which are system wide notifications.


5
Android Toast means completely different thing - it's a popup message. This library is for system-wide notifications.
Vojtěch Sázel

Should have read the comment before I installed the library just to notice then that these are not android style toasts.. Please make that clear in the answer.
findusl


1

I customised a custom popup with Rg.Plugins.Popup NuGet this is an example:

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

then in your basecontentpage you can add the following code, to show and hide the "toast" after a while:

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

0

The iOS answers above worked for me but for one little problem -- a warning: Attempt to present UIAlertController ... whose view is not in the window hierarchy!

After some search, I came across this unrelated answer which helped. The poster commented "This looks stupid but works", which is right on both counts.

So, I modified the ShowAlert() function above with these lines, which seem to work:

    var rootVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
    while ( rootVC.PresentedViewController != null) {
        rootVC = rootVC.PresentedViewController;
    }
    rootVC.PresentViewController( alert, true, null);

Dang -- I see an even better version of this below from @Pierre-Alexandre Flèche. How did I miss it before?
bobwki

0

For 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);
    }

-5

You can use DisplayAlert("", "", "", "" );


1
This doesn't at all react like a toast, it needs an action to continue.
CennoxX
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.