You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中通过CoreBluetooth移除UITableView内失效的蓝牙外设

CoreBluetooth 动态移除失效外设并更新 UITableView 的方案

核心思路:主动超时检测+被动状态监听结合

系统蓝牙设置的逻辑并非完全依赖外设状态回调(未连接的外设状态更新并不总是及时触发),而是采用主动超时检测为主、被动状态监听为辅的策略,以下是具体实现步骤:

1. 定义带超时标记的外设模型

先创建一个包含外设实例和最后发现时间的模型,用外设的唯一标识符做去重依据:

struct DiscoveredPeripheral {
    let peripheral: CBPeripheral
    var lastSeenTimestamp: TimeInterval
}

extension DiscoveredPeripheral: Hashable {
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.peripheral.identifier == rhs.peripheral.identifier
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(peripheral.identifier)
    }
}

用Set<DiscoveredPeripheral>存储已发现外设,自动避免重复。

2. 更新发现外设的逻辑

在didDiscoverPeripheral代理方法中,更新对应外设的最后发现时间,不存在则新增:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    let now = Date().timeIntervalSince1970
    let targetPeripheral = DiscoveredPeripheral(peripheral: peripheral, lastSeenTimestamp: now)
    
    if discoveredPeripherals.contains(targetPeripheral) {
        discoveredPeripherals.update(with: targetPeripheral)
    } else {
        discoveredPeripherals.insert(targetPeripheral)
        peripheral.delegate = self // 为后续状态监听做准备
    }
    tableView.reloadData()
}

3. 定时清理超时外设

启动一个定时器(比如每2秒执行一次),清理超过阈值时间未被发现的外设:

var cleanupTimer: Timer?

func startCleanupTimer() {
    cleanupTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] _ in
        guard let self = self else { return }
        let now = Date().timeIntervalSince1970
        let timeoutThreshold: TimeInterval = 5.0 // 可根据需求调整
        
        let outdatedPeripherals = self.discoveredPeripherals.filter {
            now - $0.lastSeenTimestamp > timeoutThreshold
        }
        
        guard !outdatedPeripherals.isEmpty else { return }
        
        DispatchQueue.main.async {
            outdatedPeripherals.forEach { self.discoveredPeripherals.remove($0) }
            self.tableView.reloadData()
        }
    }
}

// 扫描启动时开启定时器,停止扫描或蓝牙关闭时销毁
func centralManagerDidUpdateState(_ central: CBCentralManager) {
    if central.state == .poweredOn {
        central.scanForPeripherals(withServices: nil)
        startCleanupTimer()
    } else {
        cleanupTimer?.invalidate()
        cleanupTimer = nil
        discoveredPeripherals.removeAll()
        tableView.reloadData()
    }
}

4. 外设状态监听做补充

对于已连接的外设,监听peripheralDidUpdateState回调,当状态变为.disconnected或.failed时立即移除:

func peripheralDidUpdateState(_ peripheral: CBPeripheral) {
    if [.disconnected, .failed].contains(peripheral.state) {
        let target = DiscoveredPeripheral(peripheral: peripheral, lastSeenTimestamp: 0)
        if discoveredPeripherals.contains(target) {
            discoveredPeripherals.remove(target)
            tableView.reloadData()
        }
    }
}

为什么不单纯依赖状态更新?

  • 未连接的外设超出范围时,系统不会主动推送状态变更,只有主动扫描发现的回调能标记设备是否在线
  • 状态回调仅在设备已连接时可靠触发,无法覆盖未连接的设备场景

内容的提问来源于stack exchange,提问作者Hakan Or

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:42:22