简体   繁体   中英

Haptic Feedback crashes in Xamarin.Forms iOS Dependency Service

I am trying to use haptic feedback in a Xamarin.Forms application to inform the user of selection changes. I found references to iOS and made a dependency service for iOS; however, it always crashes without any C# error. I've tried invoking it on the main thread and the thread I'm using. It even crashes in a try/catch block. Here is my code:

using System;
namespace App.Services
{
    public enum HapticFeedbackType
    {
        ImpactHeavy, // Heavy impact
        ImpactMedium, // Medium impact
        ImpactLight, // Light impact
        Selection, // To tick while scrolling through a scrollview or carousel
        NotificationError, // When an in-app error notification occurs
        NotificationWarning, // When an in-app warning notification occurs
        NotificationSuccess // When an in-app success notification occurs
    }
    public interface IHapticFeedback
    {
        void PrepareHapticFeedback(HapticFeedbackType type);
        void ExecuteHapticFeedback(HapticFeedbackType type);
    }
}

using System;
using UIKit;

using Xamarin.Forms;

using App.Services;

[assembly: Dependency(typeof(App.iOS.Services.HapticFeedbackService))]
namespace App.iOS.Services
{
    public class HapticFeedbackService : IHapticFeedback
    {
        HapticFeedbackHelper helper;
        public HapticFeedbackService()
        {
            helper = new HapticFeedbackHelper();
        }
        public void PrepareHapticFeedback(HapticFeedbackType type)
        {
            helper.PrepareHapticFeedback(type);
        }

        public void ExecuteHapticFeedback(HapticFeedbackType type)
        {
            helper.ExecuteHapticFeedback(type);
        }
    }
    //https://blog.francois.raminosona.com/add-vibrations-in-a-xamarin-ios-app/
    public class HapticFeedbackHelper: IDisposable
    {
        private UIImpactFeedbackGenerator _impactHeavyFeedbackGenerator;
        private UIImpactFeedbackGenerator _impactMediumFeedbackGenerator;
        private UIImpactFeedbackGenerator _impactLightFeedbackGenerator;
        private UISelectionFeedbackGenerator _selectionFeedbackGenerator;
        private UINotificationFeedbackGenerator _notificationFeedbackGenerator;

        public HapticFeedbackHelper()
        {
            _impactHeavyFeedbackGenerator = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Heavy);
            _impactMediumFeedbackGenerator = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Medium);
            _impactLightFeedbackGenerator = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Light);
            _selectionFeedbackGenerator = new UISelectionFeedbackGenerator();
            _notificationFeedbackGenerator = new UINotificationFeedbackGenerator();
        }


        public void PrepareHapticFeedback(HapticFeedbackType type)
        {
            switch (type)
            {
                case HapticFeedbackType.ImpactHeavy:
                    _impactHeavyFeedbackGenerator.Prepare();
                    break;
                case HapticFeedbackType.ImpactMedium:
                    _impactMediumFeedbackGenerator.Prepare();
                    break;
                case HapticFeedbackType.ImpactLight:
                    _impactLightFeedbackGenerator.Prepare();
                    break;
                case HapticFeedbackType.Selection:
                    _selectionFeedbackGenerator.Prepare();
                    break;
                case HapticFeedbackType.NotificationError:
                case HapticFeedbackType.NotificationWarning:
                case HapticFeedbackType.NotificationSuccess:
                    _notificationFeedbackGenerator.Prepare();
                    break;
            }
        }

        public void ExecuteHapticFeedback(HapticFeedbackType type)
        {
            switch (type)
            {
                case HapticFeedbackType.ImpactHeavy:
                    _impactHeavyFeedbackGenerator.ImpactOccurred();
                    break;
                case HapticFeedbackType.ImpactMedium:
                    _impactMediumFeedbackGenerator.ImpactOccurred();
                    break;
                case HapticFeedbackType.ImpactLight:
                    _impactLightFeedbackGenerator.ImpactOccurred();
                    break;
                case HapticFeedbackType.Selection:
                    _selectionFeedbackGenerator.SelectionChanged();
                    break;
                case HapticFeedbackType.NotificationError:
                    _notificationFeedbackGenerator.NotificationOccurred(UINotificationFeedbackType.Error);
                    break;
                case HapticFeedbackType.NotificationWarning:
                    _notificationFeedbackGenerator.NotificationOccurred(UINotificationFeedbackType.Warning);
                    break;
                case HapticFeedbackType.NotificationSuccess:
                    _notificationFeedbackGenerator.NotificationOccurred(UINotificationFeedbackType.Success);
                    break;
            }
        }

        #region IDisposable
        public void Dispose()
        {
            _impactHeavyFeedbackGenerator = null;
            _impactMediumFeedbackGenerator = null;
            _impactLightFeedbackGenerator = null;
            _selectionFeedbackGenerator = null;
            _notificationFeedbackGenerator = null;
        }
        #endregion


    }
}

 async void LikeDown(object sender, MR.Gestures.DownUpEventArgs e)
        {
            LoggingController.Info("LikeDown event started...");
            if (cancelReactionShowToken?.Token != null && cancelReactionShowToken.Token.CanBeCanceled)
                cancelReactionShowToken.Cancel();

            cancelReactionShowToken = new CancellationTokenSource();
            Task task = new Task(async delegate {
                await Task.Delay(800);
                if (cancelReactionShowToken.Token.IsCancellationRequested)
                {
                    cancelReactionShowToken = null;
                    return;
                }
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    reactionPopup = new SfPopupLayout();
                    reactionPopup.PopupView.ShowHeader = false;
                    reactionPopup.PopupView.ShowFooter = false;
                    reactionPopup.PopupView.AutoSizeMode = AutoSizeMode.Both;
                    reactionPopup.PopupView.ContentTemplate = new DataTemplate(() =>
                    {
                        ReactionsView view = new ReactionsView();
                        this.Emojis = view.Emojis;
                        this.ReactionTypes = view.ReactionTypes;
                        return view;
                    });
                    reactionPopup.ShowRelativeToView(actionsRow, RelativePosition.AlignBottom, 0, 0);


                    try
                    {
                        hapticFeedback.ExecuteHapticFeedback(Services.HapticFeedbackType.Selection);

                    } catch (Exception ex) { Console.WriteLine(ex.Message); }

                //needs to be canceled so other guestures know
                cancelReactionShowToken.Cancel();
            }, cancelReactionShowToken.Token);
            task.Start();
            await task;
        }

If anyone has any experience using haptic feedback in Xamarin.Forms it would help a lot.

You need to place an exception catch-point and see exactly where the crash happens. You have placed all your code and it's difficult to figure out where you messed up. It could just be in something unrelated. You should also share your application out put that will display what the exception is.

Here's an example of the Haptic implementation by ElysiumLab.com, they also have a Nuget package that possibly has it implemented for use " Naylah ":

In your core/forms project

public class HapticFeedback
{
    public static IHapticFeedback Instance { get; set; }

    static HapticFeedback()
    {
        Instance = new DefaultHapticFeedback();
    }
}

internal class DefaultHapticFeedback : IHapticFeedback
{
    public void Run(HapticFeedbackType hapticFeedbackType)
    {
        //This is a default thing should not be used;
        //throw new System.Exception("Not initialized in device platforms isbrubles");
    }
}

public interface IHapticFeedback
{
    void Run(HapticFeedbackType hapticFeedbackType);
}

public enum HapticFeedbackType
{
    Softy,
    Medium,
    Heavy
}

iOS Implementation

public class HapticFeedbackService
{
    public static void Init()
    {
        HapticFeedback.Instance = new iOSHapticFeedback();
    }
}

public class iOSHapticFeedback : IHapticFeedback
{
    public void Run(HapticFeedbackType hapticFeedbackType)
    {
        UIImpactFeedbackGenerator impact = null;

        switch (hapticFeedbackType)
        {
            case HapticFeedbackType.Softy:
                impact = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Light);
                break;

            case HapticFeedbackType.Medium:
                impact = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Medium);
                break;

            case HapticFeedbackType.Heavy:
                impact = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Heavy);
                break;
        }

        impact.Prepare();
        impact.ImpactOccurred();
    }
}

Btw, you can also create an Android implementation for the Haptic Feedback like this:

public class HapticFeedbackService
{
    public static void Init(Activity activity)
    {
        HapticFeedback.Instance = new AndroidHapticFeedback(activity);
    }
}

internal class AndroidHapticFeedback : IHapticFeedback
{
    private readonly Activity activity;

    public AndroidHapticFeedback(Activity activity)
    {
        this.activity = activity;
    }

    public void Run(HapticFeedbackType hapticFeedbackType)
    {
        switch (hapticFeedbackType)
        {
            case HapticFeedbackType.Softy:
                activity.Window.DecorView.RootView.PerformHapticFeedback(FeedbackConstants.ContextClick);
                break;

            case HapticFeedbackType.Medium:
                activity.Window.DecorView.RootView.PerformHapticFeedback(FeedbackConstants.KeyboardPress);
                break;

            case HapticFeedbackType.Heavy:
                activity.Window.DecorView.RootView.PerformHapticFeedback(FeedbackConstants.KeyboardPress);
                break;
        }
    }
}

You can check out the detailed code here , but if you are just looking for an iOS implementation you can see here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM