制造iPhone震动

233

如何设置 iPhone 为单次震动?

例如,当玩家失去一条生命或游戏结束时,iPhone 应该进行震动。


9
摇晃手势和振动完全不同。一个是由人发起的,一个是由设备发起的。 - Eiko
12个回答

407

从"iPhone教程:更好的检查iOS设备功能的方式"中:

有两个看似相似的函数都带有参数kSystemSoundID_Vibrate

1) AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
2) AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

这两个函数都可以让iPhone震动。但是,如果你在不支持振动的设备上使用第一个函数,它会播放一声蜂鸣声。另一方面,第二个函数在不支持振动的设备上不起作用。因此,如果要将设备连续震动作为警报,常识告诉我们应该使用第二个函数。

首先,在Build Phases中将AudioToolbox框架AudioToolbox.framework添加到你的目标项目中。

然后,导入这个头文件:

#import <AudioToolbox/AudioServices.h>

3
不需要使用 #import <Cocoa/Cocoa.h>。 - Raptor
10
有没有办法将震动时间减少到不到1秒钟? - George Asda
11
如果在iOS的设置中关闭了震动,即使使用这些命令也无法使用户获得震动。请注意,翻译时不要改变原文意思,尽量使用通俗易懂的语言。 - Denis Kutlubaev
7
在 Swift 中:AudioServicesPlayAlertSound(UInt32(kSystemSoundID_Vibrate)) (至少在 beta 2 版本中)。 - Sam Soffes
1
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); 在 iPhone 上震动正常,但在 iPad 上没有任何蜂鸣声。 - Mangesh
显示剩余7条评论

46

Swift 2.0+

AudioToolbox现在将kSystemSoundID_Vibrate呈现为SystemSoundID类型,因此代码如下:

import AudioToolbox.AudioServices

AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)

无需进行额外的强制类型转换

(感谢@Dov)

原始回答(Swift 1.x)

而且,这是在Swift上执行的方法(如果您遇到了与我一样的麻烦)

链接AudioToolbox.framework(进入您的项目,选择目标,构建阶段,链接二进制文件与库,将库添加到其中)

完成后:

import AudioToolbox.AudioServices

// Use either of these
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))

内容如下:

奇怪的是,SystemSoundID本质上是一个typealias(高级Swift中的typedef),代表一个UInt32,而kSystemSoundID_Vibrate则是一个普通的Int。试图将Int转换为UInt32时,编译器会报错,但错误信息却显示“无法转换为SystemSoundID”,这很令人困惑。苹果为什么不直接将其制作成Swift枚举我就不明白了。

@aponomarenko提供了更详细的解释,我的回答只是针对那些熟悉Swift的人。


这个问题似乎在Swift 2/iOS 9/Xcode 7中已经解决了。我使用了AudioServicesPlaySystemSound(kSystemSoundID_Vibrate),并且编译通过了。 - Dov

35

一个简单的方法是使用音频服务:

#import <AudioToolbox/AudioToolbox.h> 
...    
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

3
请添加AudioToolbox.framework到项目的Build Phases中,然后导入#import <AudioToolbox/AudioToolbox.h>。 - Michael Mangold

34
我曾经在某些设备上遇到了很大的问题,因为其震动功能被关闭,但我们需要它正常工作,因为它对我们的应用程序功能至关重要。由于只是向一个已记录的方法调用传递整数,所以它将通过验证。因此,我尝试了一些在这里没有详细记录的声音:TUNER88/iOSSystemSoundsLibrary 然后我偶然发现了编号为1352的声音,无论是否静音或设备上的设置(“设置”->铃声振动,“静音”->震动)都可以正常工作。
- (void)vibratePhone;
{
     if([[UIDevice currentDevice].model isEqualToString:@"iPhone"])
     {
         AudioServicesPlaySystemSound (1352); //works ALWAYS as of this post
     }
     else
     {
          // Not an iPhone, so doesn't have vibrate
          // play the less annoying tick noise or one of your own
          AudioServicesPlayAlertSound (1105);
     }
}

7
最好使用命名别名而不是魔术常量,例如使用kSystemSoundID_Vibrate代替数字1352。我鼓励你更新你的答案。 - nalexn
3
我认同使用这个神奇的 1352 不太理想,但我找不到其他方法来在设备振动开关关闭时强制进行震动。这似乎是唯一的方法。 - marcshilling
4
我用整数编写了一个 for 循环,起始点超出了预先发布的常数,并找出了导致振动的那个整数。 - Joel Teply
2
我可以确认,即使在iPhone上启用了静音模式,iPhone也会震动。好答案! - John
2
截至2016年1月,无论静音开关位置如何,AudioServicesPlaySystemSound(1352)仍可在iPhone上使用。 - JustAnotherCoder
显示剩余4条评论

26

重要提示:未来版本可能会弃用。

iOS 9.0开始,以下API功能的描述:

AudioServicesPlaySystemSound(inSystemSoundID: SystemSoundID)
AudioServicesPlayAlertSound(inSystemSoundID: SystemSoundID)

包括以下注释:

This function will be deprecated in a future release.
Use AudioServicesPlayAlertSoundWithCompletion or  
AudioServicesPlaySystemSoundWithCompletion instead.

正确的方法是使用以下两种方法之一:

AudioServicesPlayAlertSoundWithCompletion(kSystemSoundID_Vibrate, nil)
或者
AudioServicesPlayAlertSoundWithCompletion(kSystemSoundID_Vibrate) {
 //your callback code when the vibration is done (it may not vibrate in iPod, but this callback will be always called)
}

记得要import AVFoundation


你缺少了导入语句。 - Juan Boero
导入 AudioToolbox.AudioServices。 - Hugo Alonso
我自己做的,只是用了AVFoundation。 - Juan Boero
有没有办法设置震动的持续时间? - Micro
你需要连接多个调用,并在每个新调用之间使用时间间隔。 - Hugo Alonso
弃用警告不再存在。 - Simon Pickup

12

对于 iPhone 7/7 Plus 或更新型号,使用这三个触觉反馈 API。

可用的 API

用于通知:

let generator = UINotificationFeedbackGenerator()
generator.notificationOccured(style: .error)

可用的样式包括.error.success.warning。每种样式有其独特的感觉。
来自文档

一个具体的UIFeedbackGenerator子类,创建触觉以传达成功、失败和警告。

对于简单的振动:

let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccured()

可用的样式包括.heavy(重型)、.medium(中型)和.light(轻型)。这些都是简单的振动,具有不同程度的“硬度”。
来自文档

UIFeedbackGenerator的具体子类,用于创建模拟物理效果的触觉反馈

当用户选择了一个项目时

let generator = UISelectionFeedbackGenerator()
generator.selectionChanged()

这是所有触觉反馈中最不显眼的,因此最适合用于在应用体验中不需要过多使用触觉反馈的情况。
引用自文档

UIFeedbackGenerator的一个具体子类,可以创建触感反馈以指示选择变化。

注意事项

在使用这些API时,有几件事值得记住。

注意事项A

实际上你并没有创建触觉反馈。你只是向系统请求生成触觉反馈。系统将基于以下内容进行决策:

  • 设备上是否支持触觉反馈(在这种情况下是否有触感引擎)
  • 应用程序是否可以记录音频(录制期间不会产生触觉反馈,以防止不必要的干扰)
  • 系统设置中是否启用了触觉反馈功能。

因此,如果不可能生成触觉反馈,系统将默默忽略您的请求。如果这是由于不支持的设备造成的,您可以尝试以下方法:

func haptic() {
    // Get whether the device can generate haptics or not
    // If feedbackSupportLevel is nil, will assign 0
    let feedbackSupportLevel = UIDevice.current.value(forKey: "_feedbackSupportLevel") as? Int ?? 0

    switch feedbackSupportLevel { 
    case 2:
        // 2 means the device has a Taptic Engine
        // Put Taptic Engine code here, using the APIs explained above

    case 1: 
    // 1 means no Taptic Engine, but will support AudioToolbox
    // AudioToolbox code from the myriad of other answers!

    default: // 0
        // No haptic support
        // Do something else, like a beeping noise or LED flash instead of haptics
    }
< p >替换switch-case语句中的注释,此触觉生成代码将可移植到其他iOS设备。它将生成最高级别的触觉反馈。

Note B

  • 由于生成触觉是硬件级别的任务,因此在调用触觉生成代码时可能存在延迟,这也是 Taptic Engine API 具有 prepare() 方法的原因,以使其处于准备状态。使用 Game Over 示例:您可以通过用户的生命值非常低或危险怪物接近他们来知道游戏即将结束。
  • 如果在几秒钟内未生成触觉,则 Taptic 引擎将返回空闲状态(以节省电池寿命)


在这种情况下,准备 Taptic 引擎将创建更高质量,更具响应性的体验。

例如,假设您的应用程序使用平移手势识别器来更改可见的世界部分。当用户“环顾四周”时,您希望生成触觉反馈。以下是如何使用 prepare()

@IBAction func userChangedViewablePortionOfWorld(_ gesture: UIPanGestureRecogniser!) {

    haptic = UIImpactFeedbackGenerator(style: .heavy)

    switch gesture.state {
        case .began:
            // The user started dragging the screen.
            haptic.prepare()

        case .changed:
            // The user trying to 'look' in another direction
            // Code to change viewable portion of the virtual world

            if virtualWorldViewpointDegreeMiddle = 360.0 { 
                haptic.impactOccured()
            }
        
        default:
            break

} 

只需记得 import UIKit - Benj
你不想在这个方法内创建一个新的 haptic 实例。你没有在同一个实例上调用 impactOccuredprepare - rmaddy
你好 @Benj,我想要在音频录制时实现振动,但是触觉反馈在录制时无法工作。 - Diken Shah
@DikenShah 正如所述,我相信我在某个地方读到过这是一种有意为之的举措,以最小化麦克风干扰。由于麦克风可以检测振动,而触觉引擎会产生振动,这显然会成为一个问题。 - Div
@Div 我找到了解决方案。感谢你的回答。 - Diken Shah

5
在iOS 10及更新版本的iPhone上,您还可以使用触觉API。此触觉反馈比AudioToolbox API更加柔和。
对于您的“游戏结束”场景,重型UI冲击反馈应该是合适的。
``` UIImpactFeedbackGenerator(style: .heavy).impactOccurred() ```
您可以使用其他触觉反馈样式

5
在我的旅行中,我发现如果在录制音频时尝试以下任何一项操作,即使启用了该设备,它也不会振动。
1) AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
2) AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

我在设备运动测量的特定时间调用了我的方法。我必须停止记录,然后在振动发生后重新启动记录。

效果如下所示。

-(void)vibrate {
    [recorder stop];
    AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
    [recorder start];
}

recorder 是一个 AVRecorder 实例。

希望这可以帮助其他之前遇到过同样问题的人。


5

如果你正在使用Xamarin (monotouch)框架,只需要调用以下代码

SystemSound.Vibrate.PlayAlertSound()

1
在我的情况下,我正在使用AVCaptureSession。 AudioToolbox已经在项目的构建阶段中导入了,但仍然无法工作。为了使它工作,我在震动之前停止了会话,并在此之后继续进行。
#import <AudioToolbox/AudioToolbox.h>
...
@property (nonatomic) AVCaptureSession *session;
...
- (void)vibratePhone;
{
  [self.session stopRunning];
     NSLog(@"vibratePhone %@",@"here");
    if([[UIDevice currentDevice].model isEqualToString:@"iPhone"])
    {
        AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); 
    }
    else
    {
        AudioServicesPlayAlertSound (kSystemSoundID_Vibrate);
    }
  [self.session startRunning];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接