Эквивалент тоста для 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));

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

4

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

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

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

UserDialogs.Instance.ShowLoading("Loading");

Есть ли способ переместить его наверх? Настроить и показать кратные?
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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.