關(guān)于Taptic Engine震動(dòng)反饋
What has Happened?
上周,leader 拿著 iPhone 7 打開了網(wǎng)易新聞,問我:『你看,你這里的下拉刷新是短震動(dòng),我們的手機(jī)數(shù)周遙控電視的時(shí)候只有長震動(dòng),產(chǎn)品那邊問能不能用短震動(dòng)』。
然后博主就去查看了一下關(guān)于短震動(dòng)的方式,整個(gè)過程可以描述為——『資料真少!』。
不過最后通過一下午的搜集,最終還是總結(jié)整理出來了這份文檔,也補(bǔ)充了自己對(duì) iPhone 6s 之后對(duì) Taptic Engine 的了解。
Taptic Engine
先了解一個(gè)概念——Taptic Engine
Taptic Engine 是蘋果產(chǎn)品上推出的全新震動(dòng)模塊,該元件最早出現(xiàn)在 Apple Watch 中。iPhone 6s 和 iPhone 6s Plus 中,也同樣內(nèi)置了Taptic Engine,在設(shè)計(jì)上有所升級(jí)。
Taptic Engine 振動(dòng)模塊為 Apple Watch 以及 iPhone 6s、iPhone 7 提供了 Force Touch 以及 3D Touch,不同的屏幕操作,可以感受到不同的振動(dòng)觸覺效果,帶來更好的用戶體驗(yàn)。
短震方法一 AudioServicesPlaySystemSound
常用調(diào)用:
- AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
以上代碼在各個(gè)型號(hào)手機(jī)中反應(yīng)為長震
API 系統(tǒng)版本支持:
- __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
APPLE 公開的 SystemSoundID 有:
- CF_ENUM(SystemSoundID)
- {
- kSystemSoundID_UserPreferredAlert = 0x00001000,
- kSystemSoundID_FlashScreen = 0x00000FFE,
- // this has been renamed to be consistent
- kUserPreferredAlert = kSystemSoundID_UserPreferredAlert
- };
- CF_ENUM(SystemSoundID)
- {
- kSystemSoundID_Vibrate = 0x00000FFF
- };
以上類型 沒有短震動(dòng) 。
但通過以下代碼,可以得到更多類型的震動(dòng):
- // 普通短震,3D Touch 中 Pop 震動(dòng)反饋
- AudioServicesPlaySystemSound(1520);
- // 普通短震,3D Touch 中 Peek 震動(dòng)反饋
- AudioServicesPlaySystemSound(1519);
- // 連續(xù)三次短震
- AudioServicesPlaySystemSound(1521);
但以上 ID 均未在 Apple 的 Documents 中描述。顯然,這是調(diào)用了一些私有一些屬性 。
關(guān)于是否調(diào)用了私有 API,也有一些討論,可以查看這里(https://forums.developer.apple.com/thread/45628)。
短震方法二 獲取 _tapticEngine
這種方法是從這里(https://unifiedsense.com/development/using-taptic-engine-on-ios.html)搜集到的。
- id tapticEngine = [[UIDevice currentDevice] performSelector: NSSelectorFromString(@"_tapticEngine")
- withObject:nil];
- [tapticEngine performSelector: NSSelectorFromString(@"actuateFeedback:")
- withObject:@(0)];
或者:
- id tapticEngine = [[UIDevice currentDevice] performSelector: NSSelectorFromString(@"_tapticEngine")
- withObject:nil];
- SEL selector = NSSelectorFromString(@"actuateFeedback:");
- int32_t arg = 1001;
- NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[tapticEngine methodSignatureForSelector:selector]];
- [inv setTarget:tapticEngine];
- [inv setSelector:selector];
- [inv setArgument:&arg atIndex:2];
- [inv invoke];
顯然, 這是調(diào)用了私有 API 。
這些方法,在實(shí)際測試的時(shí)候發(fā)現(xiàn),在 iPhone 7 上調(diào)用沒有震動(dòng)反饋,在 iPhone 6S Plus 上調(diào)用有震動(dòng)反饋,在 iPhone 6 上調(diào)用 無反饋。
短震方法三 UIImpactFeedbackGenerator
iOS10 引入了一種新的、產(chǎn)生觸覺反饋的方式, 幫助用戶認(rèn)識(shí)到不同的震動(dòng)反饋有不同的含義 。這個(gè)功能的核心就是由 UIFeedbackGenerator 提供。Apple 對(duì)于 UIImpactFeedbackGenerator 有一篇介紹文檔。
UIFeedbackGenerator 可以幫助你實(shí)現(xiàn) haptic feedback。它的要求是:
- 支持 Taptic Engine 機(jī)型 (iPhone 7 以及 iPhone 7 Plus).
- app 需要在前臺(tái)運(yùn)行
- 系統(tǒng) Haptics setting 需要開啟
Apple 曾表示公開了 Taptic Engine 的 API,但是鮮有文檔。在搜羅了各種資料后,可以認(rèn)為 UIImpactFeedbackGenerator 即 Taptic Engine 的 公開 API。
它的調(diào)用方式是:
- UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle: UIImpactFeedbackStyleLight];
- [generator prepare];
- [generator impactOccurred];
Others
觀察 UIImpactFeedbackGenerator 你會(huì)發(fā)現(xiàn)它繼承于 UIFeedbackGenerator。除了 UIImpactFeedbackGenerator 還有三種 FeedbackGenerator:
- UIImpactFeedbackGenerator
- UISelectionFeedbackGenerator
- UINotificationFeedbackGenerator
詳情可參考 Apple 的 這篇 Reference(https://developer.apple.com/reference/uikit/uifeedbackgenerator?language=objc) 。
對(duì)于震動(dòng)反饋的應(yīng)用,Apple 也給出了示例場景:
- - (IBAction)gestureHandler:(UIPanGestureRecognizer *)sender {
- switch (sender.state) {
- case UIGestureRecognizerStateBegan:
- // Instantiate a new generator.
- self.feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init];
- // Prepare the generator when the gesture begins.
- [self.feedbackGenerator prepare];
- break;
- case UIGestureRecognizerStateChanged:
- // Check to see if the selection has changed...
- if ([self myCustomHasSelectionChangedMethodWithTranslation:[sender translationInView: self.view]]) {
- // Trigger selection feedback.
- [self.feedbackGenerator selectionChanged];
- // Keep the generator in a prepared state.
- [self.feedbackGenerator prepare];
- }
- break;
- case UIGestureRecognizerStateCancelled:
- case UIGestureRecognizerStateEnded:
- case UIGestureRecognizerStateFailed:
- // Release the current generator.
- self.feedbackGenerator = nil;
- break;
- default:
- // Do nothing.
- break;
- }
- }
三種方法在測試機(jī)上不同的反饋結(jié)果
AUDIOSERVICESPLAYSYSTEMSOUND | 1519 | 1520 | 1521 |
---|---|---|---|
iPhone 7(iOS 10) | peek 觸感 | pop 觸感 | 三次連續(xù)短振 |
iPhone 6s Puls(iOS 9) | peek 觸感 | pop 觸感 | 三次連續(xù)短振 |
iPhone 6(iOS 10) | 無振動(dòng) | 無振動(dòng) | 無振動(dòng) |
獲取 _TAPTICENGINE | |
---|---|
iPhone 7(iOS 10) | 無振動(dòng) |
iPhone 6s Puls(iOS 9) | 長振 |
iPhone 6(iOS 10) | 無振動(dòng) |
UIIMPACTFEEDBACKGENERATOR | .LIGHT | .MEDIUM | .HEAVY |
---|---|---|---|
iPhone 7(iOS 10) | 微弱短振 | 中等短振 | 明顯短振 |
iPhone 6s Puls(iOS 9) | 長振 | 長振 | 長振 |
iPhone 6(iOS 10) | 無振動(dòng) | 無振動(dòng) | 無振動(dòng) |
總結(jié)一下,希望同樣的代碼能在更多的機(jī)型上實(shí)現(xiàn)短振,建議使用 AudioServicesPlaySystemSound(1519)。不過可能會(huì)涉及到調(diào)用私有 API。安全起見,可以使用 UIImpactFeedbackGenerator。
代碼
測試代碼在這里(https://github.com/summertian4/iOS-ObjectiveC/tree/master/iPhoneShakeDemo)。