如何在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
相关产品推荐
相关产品推荐

