MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的Core Haptics触觉反馈技术

2022-01-201.2k 阅读

1. Core Haptics简介

Core Haptics 是苹果提供的一个用于创建和管理触觉反馈的框架。触觉反馈为用户提供了一种基于触摸的直观交互体验,能够显著增强应用程序的交互性和沉浸感。无论是在游戏、导航应用还是生产力工具中,合适的触觉反馈都可以让用户更好地理解操作结果,提升用户体验。

在 iOS 13 及更高版本中,Core Haptics 框架允许开发者精确控制设备的振动电机,创建自定义的触觉模式。它提供了一组丰富的 API 来定义不同强度、持续时间和节奏的触觉反馈,使得开发者能够根据应用场景的需要,定制独特的触觉体验。

2. 设备支持

并非所有的 iOS 设备都支持 Core Haptics 所提供的全部功能。要充分利用 Core Haptics 的高级功能,设备需要配备能够产生复杂触觉反馈的 Taptic Engine。例如,iPhone 7 及后续机型具备此类硬件。在使用 Core Haptics 之前,最好先检查设备是否支持。可以通过如下代码进行检查:

if (@available(iOS 13.0, *)) {
    if ([CHHapticEngine supportsHaptics]) {
        NSLog(@"设备支持 Core Haptics 触觉反馈");
    } else {
        NSLog(@"设备不支持 Core Haptics 触觉反馈");
    }
} else {
    NSLog(@"当前系统版本不支持 Core Haptics");
}

这段代码首先使用 @available 检查当前系统是否为 iOS 13.0 或更高版本,然后调用 [CHHapticEngine supportsHaptics] 方法来判断设备是否支持 Core Haptics 触觉反馈。

3. Core Haptics 框架中的关键类

3.1 CHHapticEngine

CHHapticEngine 是 Core Haptics 框架的核心类,负责管理触觉反馈的生成和播放。它与设备的硬件进行交互,将开发者定义的触觉模式转换为实际的物理振动。要使用 CHHapticEngine,首先需要创建一个实例:

if (@available(iOS 13.0, *)) {
    NSError *error;
    CHHapticEngine *engine = [[CHHapticEngine alloc] initWithError:&error];
    if (engine) {
        NSLog(@"成功创建 CHHapticEngine 实例");
    } else {
        NSLog(@"创建 CHHapticEngine 实例失败: %@", error);
    }
}

创建实例后,需要启动引擎才能开始播放触觉反馈:

if (@available(iOS 13.0, *)) {
    [engine startAndReturnError:&error];
    if (!error) {
        NSLog(@"CHHapticEngine 已启动");
    } else {
        NSLog(@"启动 CHHapticEngine 失败: %@", error);
    }
}

当不再需要使用引擎时,记得停止并释放它:

if (@available(iOS 13.0, *)) {
    [engine stopWithCompletionHandler:^{
        [engine reset];
        engine = nil;
    }];
}

3.2 CHHapticPattern

CHHapticPattern 用于定义具体的触觉反馈模式。可以通过组合不同的事件(如脉冲、持续振动等)来创建复杂的模式。创建 CHHapticPattern 通常需要以下步骤:

首先,定义一系列的 CHHapticEvent。例如,创建一个简单的脉冲事件:

if (@available(iOS 13.0, *)) {
    CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                              relativeTime:0.0
                                                                  duration:0.1];
}

在这个例子中,CHHapticEventParameterIDAmplitude 表示事件的振幅,这里设置为 1.0(最大值),relativeTime 为 0.0 表示从模式开始时触发,duration 为 0.1 秒,表示脉冲持续时间。

然后,将这些事件组合成一个 CHHapticPattern

if (@available(iOS 13.0, *)) {
    NSArray<CHHapticEvent *> *events = @[event];
    CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:events error:&error];
    if (pattern) {
        NSLog(@"成功创建 CHHapticPattern");
    } else {
        NSLog(@"创建 CHHapticPattern 失败: %@", error);
    }
}

3.3 CHHapticPatternPlayer

CHHapticPatternPlayer 负责播放 CHHapticPattern。一旦有了 CHHapticPatternCHHapticEngine,就可以创建并使用 CHHapticPatternPlayer

if (@available(iOS 13.0, *)) {
    CHHapticPatternPlayer *player = [engine createPlayerWithPattern:pattern error:&error];
    if (player) {
        [player play];
    } else {
        NSLog(@"创建 CHHapticPatternPlayer 失败: %@", error);
    }
}

4. 创建复杂的触觉模式

4.1 组合多个事件

通过组合不同类型和参数的 CHHapticEvent,可以创建丰富多样的触觉模式。例如,创建一个先有一个强脉冲,然后是一段较弱的持续振动的模式:

if (@available(iOS 13.0, *)) {
    // 强脉冲事件
    CHHapticEvent *strongPulse = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                                  relativeTime:0.0
                                                                      duration:0.1];
    // 弱持续振动事件
    CHHapticEvent *weakVibration = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 0.5)
                                                                   relativeTime:0.1
                                                                       duration:0.5];
    NSArray<CHHapticEvent *> *events = @[strongPulse, weakVibration];
    CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:events error:&error];
    if (pattern) {
        CHHapticPatternPlayer *player = [engine createPlayerWithPattern:pattern error:&error];
        if (player) {
            [player play];
        } else {
            NSLog(@"创建 CHHapticPatternPlayer 失败: %@", error);
        }
    } else {
        NSLog(@"创建 CHHapticPattern 失败: %@", error);
    }
}

4.2 使用节奏和循环

Core Haptics 还支持通过节奏和循环来创建更复杂的触觉模式。例如,创建一个有节奏的脉冲序列:

if (@available(iOS 13.0, *)) {
    NSMutableArray<CHHapticEvent *> *rhythmicEvents = [NSMutableArray array];
    for (int i = 0; i < 5; i++) {
        CHHapticEvent *pulse = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                                relativeTime:i * 0.2
                                                                    duration:0.1];
        [rhythmicEvents addObject:pulse];
    }
    CHHapticPattern *rhythmicPattern = [[CHHapticPattern alloc] initWithEvents:rhythmicEvents error:&error];
    if (rhythmicPattern) {
        CHHapticPatternPlayer *rhythmicPlayer = [engine createPlayerWithPattern:rhythmicPattern error:&error];
        if (rhythmicPlayer) {
            [rhythmicPlayer play];
        } else {
            NSLog(@"创建 CHHapticPatternPlayer 失败: %@", error);
        }
    } else {
        NSLog(@"创建 CHHapticPattern 失败: %@", error);
    }
}

在这个例子中,通过循环创建了一系列间隔为 0.2 秒的脉冲事件,形成了一个有节奏的触觉模式。

5. 与用户交互结合

5.1 按钮点击反馈

在应用程序中,按钮点击是常见的交互操作。可以为按钮点击添加触觉反馈,增强用户体验。例如,在视图控制器中有一个按钮,当按钮被点击时触发触觉反馈:

#import "ViewController.h"
#import <CoreHaptics/CoreHaptics.h>

@interface ViewController ()

@property (nonatomic, strong) CHHapticEngine *engine;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    if (@available(iOS 13.0, *)) {
        NSError *error;
        self.engine = [[CHHapticEngine alloc] initWithError:&error];
        if (self.engine) {
            [self.engine startAndReturnError:&error];
            if (error) {
                NSLog(@"启动引擎失败: %@", error);
            }
        } else {
            NSLog(@"创建引擎失败: %@", error);
        }
    }

    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(100, 100, 200, 50);
    [button setTitle:@"点击我" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
    [self.view addButton:button];
}

- (void)buttonTapped {
    if (@available(iOS 13.0, *)) {
        CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                                  relativeTime:0.0
                                                                      duration:0.1];
        NSArray<CHHapticEvent *> *events = @[event];
        CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:events error:nil];
        CHHapticPatternPlayer *player = [self.engine createPlayerWithPattern:pattern error:nil];
        if (player) {
            [player play];
        }
    }
}

@end

5.2 手势操作反馈

对于手势操作,同样可以添加触觉反馈。比如,在用户进行长按手势时提供触觉反馈:

#import "ViewController.h"
#import <CoreHaptics/CoreHaptics.h>

@interface ViewController ()

@property (nonatomic, strong) CHHapticEngine *engine;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    if (@available(iOS 13.0, *)) {
        NSError *error;
        self.engine = [[CHHapticEngine alloc] initWithError:&error];
        if (self.engine) {
            [self.engine startAndReturnError:&error];
            if (error) {
                NSLog(@"启动引擎失败: %@", error);
            }
        } else {
            NSLog(@"创建引擎失败: %@", error);
        }
    }

    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressDetected:)];
    [self.view addGestureRecognizer:longPress];
}

- (void)longPressDetected:(UILongPressGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateBegan) {
        if (@available(iOS 13.0, *)) {
            CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                                      relativeTime:0.0
                                                                          duration:0.1];
            NSArray<CHHapticEvent *> *events = @[event];
            CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:events error:nil];
            CHHapticPatternPlayer *player = [self.engine createPlayerWithPattern:pattern error:nil];
            if (player) {
                [player play];
            }
        }
    }
}

@end

6. 音频与触觉反馈的同步

在一些应用场景中,如游戏或音乐应用,需要将音频与触觉反馈进行同步,以提供更加沉浸式的体验。Core Haptics 提供了一定的机制来实现这一点。

首先,需要在播放音频的同时,启动触觉反馈的播放。假设使用 AVAudioPlayer 播放音频:

#import <AVFoundation/AVFoundation.h>
#import <CoreHaptics/CoreHaptics.h>

@interface ViewController () <AVAudioPlayerDelegate>

@property (nonatomic, strong) AVAudioPlayer *audioPlayer;
@property (nonatomic, strong) CHHapticEngine *engine;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    if (@available(iOS 13.0, *)) {
        NSError *error;
        self.engine = [[CHHapticEngine alloc] initWithError:&error];
        if (self.engine) {
            [self.engine startAndReturnError:&error];
            if (error) {
                NSLog(@"启动引擎失败: %@", error);
            }
        } else {
            NSLog(@"创建引擎失败: %@", error);
        }
    }

    NSURL *audioURL = [[NSBundle mainBundle] URLForResource:@"audio_file" withExtension:@"mp3"];
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:&error];
    if (self.audioPlayer) {
        self.audioPlayer.delegate = self;
        [self.audioPlayer prepareToPlay];
    } else {
        NSLog(@"创建 AVAudioPlayer 失败: %@", error);
    }
}

- (IBAction)playAudioAndHaptics {
    if (self.audioPlayer && @available(iOS 13.0, *)) {
        [self.audioPlayer play];

        CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                                  relativeTime:0.0
                                                                      duration:0.1];
        NSArray<CHHapticEvent *> *events = @[event];
        CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:events error:nil];
        CHHapticPatternPlayer *player = [self.engine createPlayerWithPattern:pattern error:nil];
        if (player) {
            [player play];
        }
    }
}

@end

在这个例子中,当点击按钮触发 playAudioAndHaptics 方法时,音频和触觉反馈同时开始播放。然而,简单的同时播放可能无法精确同步,特别是在复杂的音频和触觉模式下。

为了实现更精确的同步,可以利用音频的时间信息来触发触觉事件。例如,在音频的某个特定时间点添加触觉反馈:

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (@available(iOS 13.0, *)) {
        // 假设音频播放到一半时添加触觉反馈
        NSTimeInterval audioDuration = player.duration;
        NSTimeInterval targetTime = audioDuration / 2;

        CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventParameter:CHHapticEventParameterInit(kCHHapticEventParameterIDAmplitude, 1.0)
                                                                  relativeTime:targetTime
                                                                      duration:0.1];
        NSArray<CHHapticEvent *> *events = @[event];
        CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:events error:nil];
        CHHapticPatternPlayer *hapticPlayer = [self.engine createPlayerWithPattern:pattern error:nil];
        if (hapticPlayer) {
            [hapticPlayer play];
        }
    }
}

audioPlayerDidFinishPlaying 方法中,获取音频的总时长,并在音频播放到一半的时间点创建并播放触觉反馈事件。这样可以在音频播放过程中的特定时刻精确触发触觉反馈,实现更好的同步效果。

7. 性能优化与注意事项

7.1 避免过度使用

虽然触觉反馈可以增强用户体验,但过度使用可能会导致用户不适或设备性能下降。在设计触觉反馈时,要确保其符合应用场景,并且频率和强度适中。例如,在游戏中,频繁且强烈的触觉反馈可能会让玩家感到疲劳,因此需要根据游戏节奏和操作频率来合理安排触觉反馈。

7.2 内存管理

在使用 Core Haptics 时,要注意对象的内存管理。及时释放不再使用的 CHHapticEngineCHHapticPatternCHHapticPatternPlayer 实例,避免内存泄漏。例如,在引擎不再使用时,按照如下方式进行停止和释放:

if (@available(iOS 13.0, *)) {
    [self.engine stopWithCompletionHandler:^{
        [self.engine reset];
        self.engine = nil;
    }];
}

7.3 兼容性测试

由于 Core Haptics 在不同设备和系统版本上的表现可能略有差异,因此要进行充分的兼容性测试。在开发过程中,要在多种支持 Core Haptics 的设备上进行测试,确保触觉反馈在各种情况下都能正常工作,并且体验一致。同时,也要考虑到低电量等特殊情况对触觉反馈的影响,避免出现异常行为。

8. 未来发展与应用拓展

随着技术的不断发展,Core Haptics 有望在更多领域得到应用。在虚拟现实(VR)和增强现实(AR)应用中,触觉反馈可以进一步增强用户的沉浸感。例如,在 VR 游戏中,当玩家触摸虚拟物体或受到攻击时,通过 Core Haptics 提供逼真的触觉反馈,使游戏体验更加身临其境。

在健康和健身应用中,触觉反馈可以作为一种无声的提醒或指导方式。例如,在用户进行锻炼时,通过不同模式的触觉反馈来提示用户正确的动作姿势、节奏等,而不会像声音提示那样打扰周围环境。

此外,随着智能家居的普及,Core Haptics 也可以应用于智能设备的交互中。例如,当用户触摸智能手表或智能音箱进行操作时,通过触觉反馈提供即时的确认信息,提升设备的交互友好性。

总之,Core Haptics 作为一个强大的触觉反馈框架,在 Objective - C 开发中为开发者提供了丰富的可能性,通过合理的设计和应用,可以为用户带来更加出色的交互体验,并且在未来有着广阔的发展前景。无论是在现有应用领域的深化,还是拓展到新的应用场景,Core Haptics 都将发挥重要作用。开发者需要不断探索和创新,充分挖掘其潜力,为用户创造更优质、更具沉浸感的应用体验。