检测设备是否为 iOS

IT技术 javascript ios browser browser-feature-detection
2021-02-08 13:29:26

我想知道是否有可能检测浏览器是否在 iOS 上运行,类似于使用 Modernizr 进行功能检测的方式(尽管这显然是设备检测而不是功能检测)。

通常我更喜欢功能检测,但我需要找出设备是否是 iOS,因为它们按照这个问题处理视频的方式YouTube API 不适用于 iPad / iPhone / 非 Flash 设备

6个回答

检测iOS

iOS 13 iPad 中,用户代理和平台字符串都发生了变化似乎可以区分 iPad 和 MacOS,因此下面的所有答案现在都需要考虑到这一点。

这可能是涵盖 iOS 13 的最短替代方案:

function iOS() {
  return [
    'iPad Simulator',
    'iPhone Simulator',
    'iPod Simulator',
    'iPad',
    'iPhone',
    'iPod'
  ].includes(navigator.platform)
  // iPad on iOS 13 detection
  || (navigator.userAgent.includes("Mac") && "ontouchend" in document)
}

iOS将是truefalse

更糟糕的选择:用户代理嗅探

用户代理嗅探更危险,问题经常出现。

在 iPad iOS 13 上,用户代理与 MacOS 13 计算机的用户代理相同,但如果您忽略 iPad,这可能仍然有效一段时间:

var iOS = !window.MSStream && /iPad|iPhone|iPod/.test(navigator.userAgent); // fails on iPad iOS 13

!window.MSStream是为了不错误地检测 IE11,请参阅此处此处

注意:无论navigator.userAgent并且navigator.platform可以由用户或浏览器扩展伪造。

存在用于更改 userAgent 或平台的浏览器扩展,因为网站使用过于严厉的检测并且经常禁用某些功能,即使用户的浏览器本来可以使用该功能。

为了缓和与用户的这种冲突,建议针对每种情况专门检测您的网站需要的确切功能。然后,当用户获得具有所需功能的浏览器时,它已经可以工作而无需更改额外的代码。

检测iOS版本

检测 iOS 版本的最常见方法是从 User Agent string 解析它但也有特征检测推断* ;

我们知道history APIiOS4 -matchMedia APIiOS5 -webAudio APIiOS6 -WebSpeech APIiOS7等中引入的事实

注意:以下代码不可靠,如果这些 HTML5 功能中的任何一个在较新的 iOS 版本中被弃用,则会中断。你被警告了!

function iOSversion() {

  if (iOS) { // <-- Use the one here above
    if (window.indexedDB) { return 'iOS 8 and up'; }
    if (window.SpeechSynthesisUtterance) { return 'iOS 7'; }
    if (window.webkitAudioContext) { return 'iOS 6'; }
    if (window.matchMedia) { return 'iOS 5'; }
    if (window.history && 'pushState' in window.history) { return 'iOS 4'; }
    return 'iOS 3 or earlier';
  }

  return 'Not an iOS device';
}
如果用户使用的是 Window Phone,则在用户代理字符串中测试 iPad、iPhone 或 iPod 将给出误报。此设备上的 Internet Explorer 在用户代理字符串中包含一条消息“...like iPhone OS...”,因此在此测试中将返回 true。
2021-03-17 13:29:26
从 iOS 13 开始,iPad 的用户代理已更改为“Mac OS”,例如:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15因此需要更新此答案
2021-03-21 13:29:26
您在第二个代码段中所做的是特征推断,而不是特征检测。功能检测是测试您实际要使用的功能,而您所做的是测试您碰巧知道在特定版本的操作系统中引入的功能,并从中推断出操作系统版本。这很脆弱,因为未来的 iOS 版本可能会删除这些功能。
2021-03-23 13:29:26
请注意 - navigator.platform 数组在 iPad Simulator 上不起作用,因为它在平台字符串中包含整个短语“iPad Simulator”。
2021-03-25 13:29:26
这是写支票的更好方法: var iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
2021-03-30 13:29:26

在 iOS 13 之后,您应该像这样检测 iOS 设备,因为旧的方式不会将 iPad 检测为 iOS 设备(由于新的“桌面”选项,默认情况下启用):

let isIOS = /iPad|iPhone|iPod/.test(navigator.platform)
|| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)

iOS < 13 或 iPhone 或 iPad 禁用桌面模式的第一个条件,默认配置下 iPadOS 13 的第二个条件,因为它的定位类似于 Macintosh Intel,但实际上是唯一具有多点触控的 Macintosh。

与其说是一个真正的解决方案,不如说是一个黑客,但对我来说可靠地工作

PS如前所述,您可能应该添加 IE 检查

let isIOS = (/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
!window.MSStream
@CharisTheo 因为 iPad 不在 iOS >= 13 的 userAgent 中
2021-03-11 13:29:26
navigator.maxTouchPoints 在 iOS 中不受支持,因此该检查不会为您做任何事情。
2021-03-14 13:29:26
为什么不使用navigator.userAgent此检查/iPad|iPhone|iPod/.test(navigator.platform)似乎navigator.platform总是为 iPhone iOS <= 12 返回“MacIntel”
2021-03-19 13:29:26
@PaulC,您是对的,因为 iOS 12 及更低版本未定义 maxTouchPoints,但 kikiwora 走在正确的轨道上,因为 iOS 13 支持 maxTouchPoints。请参阅我的答案。
2021-03-30 13:29:26
但是您已经在第二次检查中检查 iPad iOS >= 13 还是我遗漏了什么?
2021-04-03 13:29:26

这将变量设置_iOSDevicetruefalse

_iOSDevice = !!navigator.platform.match(/iPhone|iPod|iPad/);
@astronought 砰砰,你是布尔人 :D
2021-03-16 13:29:26
有什么用!!做?
2021-03-26 13:29:26
使用/iPhone|iPod|iPad/.test(navigator.platform)你可以避免!!
2021-03-27 13:29:26
@astronought 双否定用于转换为布尔值
2021-03-29 13:29:26

这里以前的答案都不适用于所有版本的 iOS 上的所有主要浏览器,包括 iOS 13。这是一个适用于所有 iOS 版本的 Safari、Chrome 和 Firefox 的解决方案:

var isIOS = (function () {
    var iosQuirkPresent = function () {
        var audio = new Audio();

        audio.volume = 0.5;
        return audio.volume === 1;   // volume cannot be changed from "1" on iOS 12 and below
    };

    var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
    var isAppleDevice = navigator.userAgent.includes('Macintosh');
    var isTouchScreen = navigator.maxTouchPoints >= 1;   // true for iOS 13 (and hopefully beyond)

    return isIOS || (isAppleDevice && (isTouchScreen || iosQuirkPresent()));
})();

请注意,此代码片段的编写优先考虑可读性,而不是简洁性或性能。

解释:

  • 如果用户代理包含“iPod|iPhone|iPad”中的任何一个,那么显然该设备是 iOS。否则,继续...

  • 不包含“Macintosh”的任何其他用户代理都不是 Apple 设备,因此不能是 iOS。否则,它是苹果设备,所以继续......

  • 如果maxTouchPoints值为1或更大,则 Apple 设备具有触摸屏,因此必须是 iOS 设备,因为没有带触摸屏的 Mac(感谢 kikiwora 提及maxTouchPoints)。请注意,这maxTouchPointsundefined针对 iOS 12 及更低版本的,因此我们需要针对该场景使用不同的解决方案...

  • iOS 12 及以下版本有一个在 Mac OS 中不存在的怪癖。奇怪的是,元素volume属性Audio不能成功设置为除1. 这是因为 Apple 不允许在AudioiOS 设备元素上更改音量,但在 Mac OS上允许这种怪癖可以用作区分 iOS 设备和 Mac OS 设备的最终后备方法。

@Tspoon,提供的代码片段创建了一个一次性Audio元素(如有必要)。在这种情况下,该元素实际上并不用于播放声音,并且它不会影响Audio您可能在系统中使用的其他元素的音量
2021-03-22 13:29:26
似乎这会产生实际更改非 iOS 设备上的音频音量的副作用(以防这对任何人都很重要)
2021-04-07 13:29:26

如果您使用的是Modernizr,则可以为其添加自定义测试。

无论您决定使用哪种检测模式(userAgent、navigator.vendor 或 navigator.platform),您都可以将其包装起来以便以后使用。

//Add Modernizr test
Modernizr.addTest('isios', function() {
    return navigator.userAgent.match(/(iPad|iPhone|iPod)/g);
});

//usage
if (Modernizr.isios) {
    //this adds ios class to body
    Modernizr.prefixed('ios');
} else {
    //this adds notios class to body
    Modernizr.prefixed('notios');
}
请注意,Modernizr 会自动将添加的测试名称小写。(在您的示例中,Modernizr.isiOS 永远不会返回 true)。在我看来,lib 的不良行为......
2021-03-29 13:29:26
只是很小的通知:您可以简化return x ? true : falsereturn Boolean(x)或只是return !!x
2021-04-02 13:29:26