蓝牙扫描启动 SwiftUI 后渲染列表

Posted

技术标签:

【中文标题】蓝牙扫描启动 SwiftUI 后渲染列表【英文标题】:Render List after Bluetooth Scanning starts SwiftUI 【发布时间】:2020-02-02 23:43:05 【问题描述】:

我正在尝试使用 SwiftUI 制作蓝牙扫描和连接应用程序。当蓝牙扫描开始时,我在刷新 SwiftUI 中的列表视图时遇到问题,并且我得到了一些带有 RSSI 值的外围设备名称。任何指导都会很有用。代码如下:

首先,我有一个 SwiftUI 视图,其中包含一个列表和 Horizo​​ntalView 中的文本。稍后我将使用 ForEach(),但现在我只是用一个文本保持简单。

import SwiftUI

struct ContentView: View 
    var body: some View 
        List
            // ForEach: Loop here to list all BLE Devices in "devices" array
            // Monitor "devices" array for changes. As changes happen, Render the Body again.
            HStack
                Text("Device-1")
                    .onTapGesture 
                        // To Do: Call Connect BLE Device
                        print("Device-1 Connected.")
                    
                
            .navigationBarTitle("BLE Devices")
            .onAppear(perform: connectBLEDevice)
    

    private func connectBLEDevice()
        let ble = BLEConnection()
        // Start Scanning for BLE Devices
        ble.startCentralManager()
    


// UIHosting Controller
var child = UIHostingController(rootView: ContentView())

用于扫描和连接蓝牙设备,这是我使用的代码:

import Foundation
import UIKit
import CoreBluetooth

open class BLEConnection: NSObject, CBPeripheralDelegate, CBCentralManagerDelegate 

    // Properties
    private var centralManager: CBCentralManager! = nil
    private var peripheral: CBPeripheral!

    public static let bleServiceUUID = CBUUID.init(string: "XXXX")
    public static let bleCharacteristicUUID = CBUUID.init(string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX")

    // Array to contain names of BLE devices to connect to.
    // Accessable by ContentView for Rendering the SwiftUI Body on change in this array.
    var scannedBLEDevices: [String] = []

    func startCentralManager() 
        self.centralManager = CBCentralManager(delegate: self, queue: nil)
        print("Central Manager State: \(self.centralManager.state)")
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) 
            self.centralManagerDidUpdateState(self.centralManager)
        
    

    // Handles BT Turning On/Off
    public func centralManagerDidUpdateState(_ central: CBCentralManager) 
        switch (central.state) 
           case .unsupported:
            print("BLE is Unsupported")
            break
           case .unauthorized:
            print("BLE is Unauthorized")
            break
           case .unknown:
            print("BLE is Unknown")
            break
           case .resetting:
            print("BLE is Resetting")
            break
           case .poweredOff:
            print("BLE is Powered Off")
            break
           case .poweredOn:
            print("Central scanning for", BLEConnection.bleServiceUUID);
            self.centralManager.scanForPeripherals(withServices: [BLEConnection.bleServiceUUID],options: [CBCentralManagerScanOptionAllowDuplicatesKey : true])
            break
        

       if(central.state != CBManagerState.poweredOn)
       
           // In a real app, you'd deal with all the states correctly
           return;
       
    


    // Handles the result of the scan
    public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 
        print("Peripheral Name: \(String(describing: peripheral.name))  RSSI: \(String(RSSI.doubleValue))")
        // We've found it so stop scan
        self.centralManager.stopScan()
        // Copy the peripheral instance
        self.peripheral = peripheral
        self.scannedBLEDevices.append(peripheral.name!)
        self.peripheral.delegate = self
        // Connect!
        self.centralManager.connect(self.peripheral, options: nil)
    


    // The handler if we do connect successfully
    public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 
        if peripheral == self.peripheral 
            print("Connected to your BLE Board")
            peripheral.discoverServices([BLEConnection.bleServiceUUID])
        
    


    // Handles discovery event
    public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) 
        if let services = peripheral.services 
            for service in services 
                if service.uuid == BLEConnection.bleServiceUUID 
                    print("BLE Service found")
                    //Now kick off discovery of characteristics
                    peripheral.discoverCharacteristics([BLEConnection.bleCharacteristicUUID], for: service)
                    return
                
            
        
    

    // Handling discovery of characteristics
    public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) 
        if let characteristics = service.characteristics 
            for characteristic in characteristics 
                if characteristic.uuid == BLEConnection.bleServiceUUID 
                    print("BLE service characteristic found")
                 else 
                    print("Characteristic not found.")
                
            
        
    

这里的任务是扫描外围设备,并在它们从 SwiftUI 列表的范围内进出时显示它们。 谢谢。

【问题讨论】:

您愿意分享您的完整(完成)代码吗?原因是您似乎使用 SwiftUI 和 BLE 创建了一个更新的示例。 如果你还有代码的话,还要找一个 SwiftUI 和 BLE 的例子 您介意分享您已完成(已解决)的项目并提供链接吗? @Anuj 大家好。我无法共享代码,因为它与公司代码混杂在一起,需要一些时间和精力来清理它。但是,您可以在这里查看类似的方法:novelbits.io/intro-ble-mobile-development-ios-part-2 【参考方案1】:

您在这里没有状态,也无法更新该状态。我可能会将BLEConnection 设为ObservableObject,然后将@Publish 设为设备数组:

open class BLEConnection: ..., ObservableObject 
    @Publish var scannedBLEDevices: [Device] = [] // keep as [String] for initial debugging if you want

然后,在您的 ContentView 中订阅这些更改:

struct ContentView: View 
    @ObservedObject var bleConnection = BLEConnection()

    var body: some View 
        // if still `[String]` then \.self will work, otherwise make `Device` `Identifiable`
        List(bleConnection.devices, id: \.self)  device in
            Text(verbatim: device)
        
    

现在,当您的连接向scannedBLEDevices 添加/删除设备时,它会自动在您的ContentView 中更新。

【讨论】:

感谢分享!正是我想要的! 我认为@Publish 应该是@Published 我需要完整的代码?

以上是关于蓝牙扫描启动 SwiftUI 后渲染列表的主要内容,如果未能解决你的问题,请参考以下文章

Swift:为啥我的 iOS 不能扫描其他蓝牙设备

缓存的 Android 蓝牙设备

您如何在 Swift Playgrounds 中的 iPad 上的 SwiftUI 中渲染 Text("Content") 与 Mac 上的 Xcode?

swiftui 请求 渲染数据

SwiftUI - 从 Swift 类启动的可观察对象不会更新 ContentView() 上的 @ObservedObject

Android 低功耗蓝牙开发(扫描连接数据交互)Kotlin版