Эквивалент тоста для Xamarin Forms

89

Есть ли способ использовать Xamarin Forms (не специфичные для Android или iOS) для отображения всплывающего окна, как в Android с Toast, которое не требует взаимодействия с пользователем и исчезает через (короткий) период времени?

Из поиска вокруг все, что я вижу, - это предупреждения, для устранения которых требуется щелчок пользователя.

Джимми
источник

Ответы:

170

Для этого есть простое решение. Используя DependencyService, вы можете легко получить подход Toast-Like как в Android, так и в iOS.

Создайте интерфейс в вашем общем пакете.

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

Раздел 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();
        }
    }
}

раздел iOS

В iOs нет собственного решения вроде Toast, поэтому нам нужно реализовать собственный подход.

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

Обратите внимание, что на каждой платформе мы должны зарегистрировать наши классы в DependencyService.

Теперь вы можете получить доступ к сервису Toast в любом месте нашего проекта.

DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);
Алекс Ченгалан
источник
20
Это, безусловно, лучший ответ на этот вопрос. Никаких сторонних плагинов или библиотек не требуется.
Брет Фаллер
4
в строке DependencyService я получаю «Ссылка на объект не установлена ​​на экземпляр объекта».
Джойс де Ланна
5
Да, это лучший ответ на данный момент, мне нравится служба зависимости,
Лутая Хузайфа Идрис
1
Полный победы. У вас тоже есть версия для UWP?
Ниеминен
1
@MengTim Кажется, я исправил зависший пользовательский интерфейс, каждый раз создавая новое предупреждение и таймер. Оба должны быть переданы DismissMessage.
Ян Уорбертон
13

Вот версия IOS Alex Chengalan в коде , что позволяет избежать UI торчит , когда несколько сообщений показаны ...

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();
            }
        }
    }
Ян Уорбертон
источник
10

Вы можете использовать пакет Acr.UserDialogs из nuget и кода, как показано ниже,

Acr.UserDialogs.UserDialogs.Instance.Toast(Message, new TimeSpan(3));
KiShOrE
источник
9

Обычно мы использовали плагин Egors Toasts, но, поскольку он требует разрешений на iOS для текущего проекта, мы пошли другим путем, используя nuget Rg.Plugins.Popup ( https://github.com/rotorgames/Rg.Plugins.Popup ).

Я написал базовую страницу xaml / cs типа 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">
...

и создать его с помощью службы, чей интерфейс вы регистрируете при запуске приложения или используете Xamarin.Forms.DependencyService для получения службы, также будет жизнеспособным.

Служба выводит новости на производную страницу PopupPage и делает

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

Пользователь может закрыть всплывающую страницу, нажав за пределами отображения страницы (при условии, что она не заполнила экран).

Кажется, что это хорошо работает на iOS / Droid, но я открыт для исправления, если кто-нибудь знает, что это рискованный способ сделать это.

Аллистер
источник
rg всплывающие окна великолепны. Я делаю аналогичный обходной путь для загрузки дисплея или индикатора активности на всю страницу. Проблема с другими плагинами заключается в том, что они зависят от асинхронных функций и основного потока, но всплывающее окно rg может работать во втором потоке, что делает его очень полезным. действительно хорошая идея, но я хочу, чтобы нативный вид был похож на тосты Android.
Emil
На данный момент это лучший метод создания тостов на разных платформах. Rg.Popup очень гибкий, и я использую его почти в каждом проекте. Нет необходимости использовать другие плагины или код платформы для отображения тостов,
Джампаоло
8

Добавляя к ответу Алекса, вот вариант 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();
  }
}

Расцветка и укладка на ваше усмотрение, на MaxHeightсамом деле требуется, чтобы высота была минимальной.

Габор
источник
Значит, для UWP не нужно регистрировать его как службу зависимостей?
Олорунфеми Аджибулу
Работает точно так же, как и два других варианта. Да, служба зависимости.
Gábor
7

Вы можете использовать IUserDialog NuGet и просто использовать его toastAlert

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

UserDialogs.Instance.Toast(toastConfig);
Мохаммад Камали
источник
4

Вот фрагмент кода, который я использую, чтобы показать тост в 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();
    }
Дурай Амутан.H
источник
4

Я бы порекомендовал Plugin.Toastбиблиотеку от nuget. Это работает хорошо.

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

или из библиотеки Nuget ACR.UserDialogs

UserDialogs.Instance.ShowLoading("Loading");
Fk Bey
источник
Есть ли способ переместить его наверх? Настроить и показать кратные?
G_Money
нет. эта библиотека поддерживает только базовые всплывающие сообщения. вы просто можете изменить bg и цвет текста, а также продолжительность сообщения.
Fk Bey
4

@MengTim, чтобы исправить проблему с несколькими тостами в решении @ alex-chengalan, я просто обернул все внутри ShowAlert()проверкой, чтобы увидеть, являются ли alertи равными alertDelayнулю, затем внутри DismissMessage, обнулено alertи 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;
        }
    }

По крайней мере, это помогло устранить зависание пользовательского интерфейса, если вы ищете быстрое решение. Я пытался отобразить тост при переходе на новую страницу и считаю, PresentViewControllerчто установка по существу отменяет мою навигацию. Извините, я не комментировал в теме, моя репутация слишком низкая :(

Гуннар
источник
1

В Forms нет встроенного механизма, но этот пакет nuget предоставляет нечто подобное

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

Примечание. Это тосты не в стиле Android, как указано в вопросе, а в стиле UWP, которые являются общесистемными уведомлениями.

Джейсон
источник
5
Android Toast означает совершенно другое - это всплывающее сообщение. Эта библиотека предназначена для общесистемных уведомлений.
Vojtěch Sázel
Надо было прочитать комментарий перед установкой библиотеки, чтобы заметить, что это не тосты в стиле Android. Пожалуйста, поясните это в ответе.
findusl
1

Это моя улучшенная ShowAlertверсия версии Яна Уорбертона, которая гарантирует, что тосты будут отображаться даже на всплывающей странице. Кроме того, тост исчезает, если пользователь щелкает за пределами тоста. Я использовал UIAlertControllerStyle.ActionSheetэтот вид как тост, но он также работает с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);
        });
    }

Надеюсь, это кому-то поможет!

Пьер-Александр Флеш
источник
1

Я настроил настраиваемое всплывающее окно с помощью Rg.Plugins.Popup NuGet, это пример:

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

затем на своей базовой странице вы можете добавить следующий код, чтобы через некоторое время показать и скрыть «тост»:

public async void showpopup(string msg)
    {
        await Navigation.PushPopupAsync(new Toast(msg));
        await Task.Delay(3000);
        await Navigation.PopPopupAsync(true);   
    }
Патрисия Лопес
источник
0

Приведенные выше ответы iOS сработали для меня, но с одной маленькой проблемой - предупреждением: попытка представить UIAlertController ... чье представление не находится в иерархии окон!

После некоторого поиска я наткнулся на этот несвязанный ответ, который помог. Автор сообщения прокомментировал: «Это выглядит глупо, но работает», что верно по обоим пунктам.

Итак, я изменил приведенную выше функцию ShowAlert () следующими строками, которые, похоже, работают:

    var rootVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
    while ( rootVC.PresentedViewController != null) {
        rootVC = rootVC.PresentedViewController;
    }
    rootVC.PresentViewController( alert, true, null);
бобвки
источник
Черт, я вижу еще лучшую версию этого ниже от @ Pierre-Alexandre Flèche. Как я это пропустил раньше?
bobwki
0

Для 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

Ты можешь использовать DisplayAlert("", "", "", "" );

О. Ахмадпур
источник
1
Это совсем не похоже на тост, для продолжения нужно действие.
CennoxX