简体   繁体   English

检测 Apple Pencil 是否连接到 iPad Pro

[英]Detect whether Apple Pencil is connected to an iPad Pro

Is there an API that allows you to determine whether the Apple Pencil is connected to an iPad Pro?是否有 API 可以让您确定 Apple Pencil 是否连接到 iPad Pro? Looking over the 9.1 SDK I don't see anything that directly does this.查看 9.1 SDK,我没有看到任何直接执行此操作的内容。 Or perhaps this can be done using the Bluetooth API.或者这可以使用蓝牙 API 来完成。

I can't find any actual documentation on the Apple Pencil's Bluetooth implementation (and I don't believe any exists), but the following code Works for Me™.我找不到有关 Apple Pencil 蓝牙实现的任何实际文档(我认为不存在任何文档),但以下代码适用于我™。

It checks for connected devices that advertise themselves as supporting the "Device Information" service and then if any of these have the name "Apple Pencil".它会检查宣传自己支持“设备信息”服务的已连接设备,然后检查这些设备中是否有任何名称为“Apple Pencil”。

PencilDetector.h铅笔检测器.h

@import CoreBluetooth

@interface PencilDetector : NSObject <CBCentralManagerDelegate>

- (instancetype)init;

@end

PencilDetector.m PencilDetector.m

#include "PencilDetector.h"

@interface PencilDetector ()

@end

@implementation PencilDetector
{
  CBCentralManager* m_centralManager;
}

- (instancetype)init
{
  self = [super init];
  if (self != nil) {
    // Save a reference to the central manager. Without doing this, we never get
    // the call to centralManagerDidUpdateState method.
    m_centralManager = [[CBCentralManager alloc] initWithDelegate:self
                                                            queue:nil
                                                          options:nil];
  }

  return self;
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
  if ([central state] == CBCentralManagerStatePoweredOn)
  {
    // Device information UUID
    NSArray* myArray = [NSArray arrayWithObject:[CBUUID UUIDWithString:@"180A"]];

    NSArray* peripherals =
      [m_centralManager retrieveConnectedPeripheralsWithServices:myArray];
    for (CBPeripheral* peripheral in peripherals)
    {
        if ([[peripheral name] isEqualToString:@"Apple Pencil"])
        {
            // The Apple pencil is connected
        }
    }
  }
}

@end

In practice, the following, simpler, synchronous code, which doesn't wait for the central manager to be powered on before checking for connected devices seems to work just as well in my testing.在实践中,以下更简单的同步代码在检查连接的设备之前不等待中央管理器开机,在我的测试中似乎也能正常工作。 However, the documentation states that you shouldn't call any methods on the manager until the state has updated to be CBCentralManagerStatePoweredOn , so the longer code is probably safer.但是,文档指出,在状态更新为CBCentralManagerStatePoweredOn之前,您不应在管理器上调用任何方法,因此较长的代码可能更安全。

Anywhere you like你喜欢的任何地方

m_centralManager = [[CBCentralManager alloc] initWithDelegate:nil
                                                        queue:nil
                                                      options:nil];

// Device information UUID
NSArray* myArray = [NSArray arrayWithObject:[CBUUID UUIDWithString:@"180A"]];

NSArray* peripherals =
  [m_centralManager retrieveConnectedPeripheralsWithServices:myArray];
for (CBPeripheral* peripheral in peripherals)
{
  if ([[peripheral name] isEqualToString:@"Apple Pencil"])
  {
    // The Apple pencil is connected
  }
}

Took me quite a while to figure out that CBCentralManager's centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) is only called when the connection is initiated via its connect(_ peripheral: CBPeripheral, options: [String : Any]? = nil) function (yes, reading the docs helps :]).我花了很长时间才弄清楚 CBCentralManager 的centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)仅在通过其connect(_ peripheral: CBPeripheral, options: [String : Any]? = nil)发起连接时才被调用功能(是的,阅读文档有帮助:])。

Since we have no callback for when devices have been connected to the device through the user (as is the case with Apple Pencil - I'd love to be proven wrong on this one btw), I had to resort to using a timer here.由于我们没有通过用户将设备连接到设备的回调(就像 Apple Pencil 的情况一样 - 顺便说一句,我很想证明这是错误的),我不得不在这里使用计时器。

This is how it works:这是它的工作原理:

When you initialize ApplePencilReachability a timer is setup that checks for the availability of the pencil every second.当您初始化ApplePencilReachability会设置一个计时器, ApplePencilReachability检查铅笔的可用性。 If a pencil is found the timer gets invalidated, if bluetooth is turned off it also gets invalidated.如果找到铅笔,计时器就会失效,如果蓝牙关闭,它也会失效。 When it's turned on again a new timer is created.当它再次打开时,会创建一个新的计时器。

I am not particularly proud of it but it works :-)我不是特别自豪,但它有效:-)

import CoreBluetooth

class ApplePencilReachability: NSObject, CBCentralManagerDelegate {

  private let centralManager = CBCentralManager()
  var pencilAvailabilityDidChangeClosure: ((_ isAvailable: Bool) -> Void)?

  var timer: Timer? {
    didSet {
      if oldValue !== timer { oldValue?.invalidate() }
    }
  }

  var isPencilAvailable = false {
    didSet { 
      guard oldValue != isPencilAvailable else { return }
      pencilAvailabilityDidChangeClosure?(isPencilAvailable)
    }
  }

  override init() {
    super.init()
    centralManager.delegate = self
    centralManagerDidUpdateState(centralManager) // can be powered-on already?
  }
  deinit { timer?.invalidate() }

  func centralManagerDidUpdateState(_ central: CBCentralManager) {
    if central.state == .poweredOn {
      timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { 
        [weak self] timer in // break retain-cycle
        self?.checkAvailability()
        if self == nil { timer.invalidate() }
      }
    } else {
      timer = nil
      isPencilAvailable = false
    }
  }

  private func checkAvailability() {
    let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: "180A")])
    let oldPencilAvailability = isPencilAvailable
    isPencilAvailable = peripherals.contains(where: { $0.name == "Apple Pencil" })
    if isPencilAvailable {
      timer = nil // only if you want to stop once detected
    }
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM