iOS必须用plus.ios调UIImpactFeedbackGenerator实现可控触感,Android仅能控时长且受厂商严格限制;iOS真机无震因系统触感开关未开或静音拨片启用,uni.vibrateShort在iOS无效。

直接说结论:iOS 上想实现真正可控的点击触感,必须放弃 uni.vibrateShort,改用 plus.ios 调 UIImpactFeedbackGenerator;Android 只能控制时长(如 plus.device.vibrate(15)),所谓“强度”是错觉,且受厂商限制极严。
iOS 真机没震动?不是代码问题,是系统开关没开
iOS 完全不认 uni.vibrateShort() 的“短震”语义。它要么静默失败,要么退化成约 400ms 的长震(等效 uni.vibrateLong())。根本原因有两个硬性条件:
- 「设置 → 辅助功能 → 触感反馈」必须开启(无法用代码检测或弹窗请求)
- 侧边静音拨片不能处于静音状态(静音时所有触感调用被系统丢弃)
更关键的是:uni.vibrateShort({ duration: 10 }) 这类带参数的写法在 iOS 上会被直接忽略——连日志都不会打。你看到“没反应”,大概率是用户根本没开那个开关。
想在 iOS 实现三档触感(轻/中/重),必须用 plus.ios 原生桥接
绕过 uni-app 封装,直连 iOS 原生触感引擎是唯一可靠路径。注意以下实操要点:
- 仅限
#ifdef APP-PLUS && (IOS || iOS)条件编译块内使用,否则安卓打包报错 - 必须按顺序调用:
impact.prepare()→impact.impactOccurred(),漏掉prepare()会静默失败 - 风格必须用明确常量:
UIImpactFeedbackStyleLight、UIImpactFeedbackStyleMedium、UIImpactFeedbackStyleHeavy,别传数字或字符串魔数 - 每次触发建议 new 新实例,iOS 16+ 对复用有限制,多次调用后可能失效
示例片段:
const impact = plus.ios.importClass('UIImpactFeedbackGenerator').alloc().init()
impact.prepare()
impact.impactOccurred()
Android 震动不是“调强度”,而是控时长+防丢弃
Android 底层 Vibrator 不暴露振幅接口,“轻/重”只是人对时长和节奏的主观感受:
-
plus.device.vibrate(15)是最短可用值,低于 10ms 系统会截断 - 连续震动必须间隔 ≥ 50ms,否则被系统合并或丢弃(比如快速连点 3 次,可能只震 1 次)
- Android 8.0+ 必须在
manifest.json的androidPermissions中显式声明"android.permission.VIBRATE",且运行时需用户授权 - 华为 EMUI、小米 HyperOS 等 ROM 还要求手动打开「应用通知权限 → 振动提醒」,否则静默失败
别信 uni.vibrate({ type: 'light' }) ——该写法只在 uni-app 3.99+ 版本中被识别,老项目(Vue2 或 3.7.x)会直接忽略整个对象参数,退化为无参调用,结果不可控。
跨平台封装建议:别强求一致,让各端走最合适的路
不要试图用一个 API 同时满足 iOS 和 Android 的“强度调节”。正确做法是封装一个函数,按平台分流:
- iOS:走
plus.ios+UIImpactFeedbackGenerator,分 Light/Medium/Heavy 三档 - Android:走
plus.device.vibrate(15)或plus.device.vibrate(400),对应短/长震 - H5 / 小程序:直接降级为视觉反馈(如按钮缩放动画 +
uni.showToast)
最容易被忽略的一点:iOS 触感是否开启,你完全无法通过代码判断,也不能触发系统弹窗。只能在首次使用前,用图文弹窗引导用户去「设置 → 辅助功能 → 触感反馈」手动打开——这步跳过,后面所有原生触感代码都是白写。


















