iOS——关于-Taptic-Engine-震动反馈

huchao007 8年前
   <p>上周,leader 拿着 iPhone 7 打开了网易新闻,问我:『你看,你这里的下拉刷新是 短震动 ,我们的手机数周遥控电视的时候只有 长震动 ,产品那边问能不能用短震动』。</p>    <p>然后博主就去查看了一下关于短震动的方式,整个过程可以描述为——『资料真少!』。</p>    <p>不过最后通过一下午的搜集,最终还是总结整理出来了这份文档,也补充了自己对 iPhone 6s 之后对 Taptic Engine 的了解。</p>    <h2>Taptic Engine</h2>    <p>先了解一个概念——Taptic Engine</p>    <p>Taptic Engine 是苹果产品上推出的全新震动模块,该元件最早出现在 Apple Watch 中。iPhone 6s 和 iPhone 6s Plus 中,也同样内置了Taptic Engine,在设计上有所升级。</p>    <p>Taptic Engine 振动模块为 Apple Watch 以及 iPhone 6s、iPhone 7 提供了 Force Touch 以及 3D Touch,不同的屏幕操作,可以感受到不同的振动触觉效果,带来更好的用户体验。</p>    <h2>短震方法一 AudioServicesPlaySystemSound</h2>    <p>常用调用:</p>    <pre>  <code class="language-objectivec">AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);  </code></pre>    <p>以上代码在各个型号手机中反应为长震</p>    <p>API 系统版本支持:</p>    <pre>  <code class="language-objectivec">__OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);  </code></pre>    <p>APPLE 公开的 SystemSoundID 有:</p>    <pre>  <code class="language-objectivec">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  };  </code></pre>    <p>以上类型 <em> <strong>没有短震动</strong> </em> 。</p>    <p>但通过以下代码,可以得到更多类型的震动:</p>    <pre>  <code class="language-objectivec">// 普通短震,3D Touch 中 Peek 震动反馈  AudioServicesPlaySystemSound(1519);  </code></pre>    <pre>  <code class="language-objectivec">// 普通短震,3D Touch 中 Pop 震动反馈  AudioServicesPlaySystemSound(1520);  </code></pre>    <pre>  <code class="language-objectivec">// 连续三次短震  AudioServicesPlaySystemSound(1521);  </code></pre>    <p>但以上 ID 均未在 Apple 的 Documents 中描述。显然, <em> <strong>这是调用了一些私有一些属性</strong> </em> 。</p>    <h2>短震方法二 获取 _tapticEngine</h2>    <p> </p>    <pre>  <code class="language-objectivec">id tapticEngine = [[UIDevice currentDevice] performSelector: NSSelectorFromString(@"_tapticEngine")                                                       withObject:nil];  [tapticEngine performSelector: NSSelectorFromString(@"actuateFeedback:")                         withObject:@(0)];  </code></pre>    <p>或者:</p>    <pre>  <code class="language-objectivec">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];  </code></pre>    <p>显然, <em> <strong>这是调用了私有 API</strong> </em> 。</p>    <p>这些方法,在实际测试的时候发现,在 iPhone 7 上调用没有震动反馈,在 iPhone 6S Plus 上调用有震动反馈,在 iPhone 6 上调用 无反馈。</p>    <h2>短震方法三 UIImpactFeedbackGenerator</h2>    <p>iOS10 引入了一种新的、产生触觉反馈的方式, <em> <strong>帮助用户认识到不同的震动反馈有不同的含义</strong> </em> 。这个功能的核心就是由 UIFeedbackGenerator 提供。</p>    <p>UIFeedbackGenerator 可以帮助你实现 haptic feedback。它的要求是:</p>    <ol>     <li>支持 Taptic Engine 机型 (iPhone 7 以及 iPhone 7 Plus).</li>     <li>app 需要在前台运行</li>     <li>系统 Haptics setting 需要开启</li>    </ol>    <p>Apple 曾表示公开了 Taptic Engine 的 API,但是鲜有文档。在搜罗了各种资料后,可以认为 UIImpactFeedbackGenerator 即 Taptic Engine 的 公开 API。</p>    <p>它的调用方式是:</p>    <pre>  <code class="language-objectivec">UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle: UIImpactFeedbackStyleLight];  [generator prepare];  [generator impactOccurred];  </code></pre>    <h2>Others</h2>    <p>观察 UIImpactFeedbackGenerator 你会发现它继承于 UIFeedbackGenerator 。除了 UIImpactFeedbackGenerator 还有三种 FeedbackGenerator:</p>    <ol>     <li>UIImpactFeedbackGenerator</li>     <li>UISelectionFeedbackGenerator</li>     <li>UINotificationFeedbackGenerator</li>    </ol>    <p>对于震动反馈的应用,Apple 也给出了示例场景:</p>    <pre>  <code class="language-objectivec">- (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;      }  }  </code></pre>    <h2>三种方法在测试机上不同的反馈结果</h2>    <table>     <thead>      <tr>       <th>AudioServicesPlaySystemSound</th>       <th>1519</th>       <th>1520</th>       <th>1521</th>      </tr>     </thead>     <tbody>      <tr>       <td>iPhone 7(iOS 10)</td>       <td>peek 触感</td>       <td>pop 触感</td>       <td>三次连续短振</td>      </tr>      <tr>       <td>iPhone 6s Puls(iOS 9)</td>       <td>peek 触感</td>       <td>pop 触感</td>       <td>三次连续短振</td>      </tr>      <tr>       <td>iPhone 6(iOS 10)</td>       <td>无振动</td>       <td>无振动</td>       <td>无振动</td>      </tr>     </tbody>    </table>    <table>     <thead>      <tr>       <th>获取 _tapticEngine</th>       <th> </th>      </tr>     </thead>     <tbody>      <tr>       <td>iPhone 7(iOS 10)</td>       <td>无振动</td>      </tr>      <tr>       <td>iPhone 6s Puls(iOS 9)</td>       <td>长振</td>      </tr>      <tr>       <td>iPhone 6(iOS 10)</td>       <td>无振动</td>      </tr>     </tbody>    </table>    <table>     <thead>      <tr>       <th>UIImpactFeedbackGenerator</th>       <th>.Light</th>       <th>.Medium</th>       <th>.Heavy</th>      </tr>     </thead>     <tbody>      <tr>       <td>iPhone 7(iOS 10)</td>       <td>微弱短振</td>       <td>中等短振</td>       <td>明显短振</td>      </tr>      <tr>       <td>iPhone 6s Puls(iOS 9)</td>       <td>长振</td>       <td>长振</td>       <td>长振</td>      </tr>      <tr>       <td>iPhone 6(iOS 10)</td>       <td>无振动</td>       <td>无振动</td>       <td>无振动</td>      </tr>     </tbody>    </table>    <p>总结一下,希望同样的代码能在更多的机型上实现短振,建议使用 AudioServicesPlaySystemSound(1519)。不过可能会涉及到调用私有 API。安全起见,可以使用 UIImpactFeedbackGenerator 。</p>    <p> </p>    <p>来自:http://zhoulingyu.com/2017/01/16/iOS——关于-Taptic-Engine-震动反馈/</p>    <p> </p>