Xamarin.Forms iOS 依赖服务中的触觉反馈崩溃

Posted

技术标签:

【中文标题】Xamarin.Forms iOS 依赖服务中的触觉反馈崩溃【英文标题】:Haptic Feedback crashes in Xamarin.Forms iOS Dependency Service 【发布时间】:2020-08-08 07:21:04 【问题描述】:

我正在尝试在 Xamarin.Forms 应用程序中使用触觉反馈来通知用户选择更改。我找到了对 ios 的引用,并为 iOS 做了一个依赖服务;但是,它总是在没有任何 C# 错误的情况下崩溃。我试过在主线程和我正在使用的线程上调用它。它甚至在 try/catch 块中崩溃。这是我的代码:

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;
        

如果有人对在 Xamarin.Forms 中使用触觉反馈有任何经验,那将会很有帮助。

【问题讨论】:

Xamarin Essentials 有一个振动库,可以满足您的需求。如果没有,您需要首先确定导致崩溃的行。 振动不起作用,因为它太强大了。触觉反馈是在选择器中使用的。它非常温和,并且有不同类型的振动。每当用户在图表上滑动手指或需要对某种次要事件的反馈时,都会调用它。振动适用于主要的事情,例如电话等 【参考方案1】:

您需要放置一个异常捕获点并准确查看崩溃发生的位置。您已经放置了所有代码,很难弄清楚您在哪里搞砸了。它可能只是在一些不相关的东西中。您还应该分享您的应用程序输出,以显示异常是什么。

这是 ElysiumLab.com 的 Haptic 实现示例,他们还有一个 Nuget 包,可能已实现它以供使用“Naylah”:

在您的核心/表单项目中

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

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

顺便说一句,您还可以像这样为 Haptic Feedback 创建一个 android 实现:

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;
        
    

您可以查看详细代码here,但如果您只是在寻找iOS实现,您可以查看here。

【讨论】:

我从博客中复制的实现和这相似,但使用它并没有崩溃。而且我无法粘贴应用程序输出,因为它在 VS 中没有给我任何信息,很可能是一些低级 iOS 系统错误。 嗯,奇怪。是的,它们非常相似,我很高兴它现在起作用了!

以上是关于Xamarin.Forms iOS 依赖服务中的触觉反馈崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何使用依赖注入在 Xamarin.Forms 中实现 Flyoutnavigation

Xamarin.Forms DependencyService 始终返回 null

使用依赖服务从 Xamarin.Forms 应用发送电子邮件

使用 Prism 在 Xamarin Forms 的后台服务中实现依赖注入

Xamarin.Forms 无法更改本地设备上 Xamarin.iOS 中的应用程序图标

Xamarin Forms - 状态栏与 IOS 中的内容重叠(safeareainsets 问题)