【问题标题】:Scan for BLE devices and present them in an UITableView扫描 BLE 设备并将它们呈现在 UITableView 中
【发布时间】:2016-02-13 11:12:47
【问题描述】:

我试图找到一种方法来扫描 BLE 设备并将它们呈现在 UITableView 中。 BLE 设备的扫描、连接、读取和写入功能清晰且有效!所以我的问题集中在“ScanTableView”和“BletoothManager”类之间的交互上。

这是我的两个课程:

//  ScanTableView.swift

import UIKit

class ScanTableView: UITableViewController {

    @IBOutlet var scanTableView: UITableView!

    var bluetoothManager = BluetoothManager?()
    var tableViewScanTime = 5
    var timer1: NSTimer!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.refreshControl!.addTarget(self, action: "refresh", forControlEvents: UIControlEvents.ValueChanged)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let _ = bluetoothManager?.peripheralArray.count {
            return bluetoothManager!.peripheralArray.count
        }
        else {
            return 0
        }
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = scanTableView.dequeueReusableCellWithIdentifier("scanCell",forIndexPath: indexPath)
        cell.textLabel!.text = bluetoothManager!.peripheralArray[indexPath.row].name
        cell.detailTextLabel!.text = bluetoothManager!.peripheralArray[indexPath.row].RSSI
        return cell
    }

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        bluetoothManager!.selectedPeripheral = bluetoothManager!.peripheralArray[indexPath.row]
        bluetoothManager!.connectPeripheral(bluetoothManager!.selectedPeripheral!)
    }

    func refresh() {
        scanTableView.userInteractionEnabled = false
        timer1 = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "scanTableViewRefresh", userInfo: nil, repeats: true)
        bluetoothManager = BluetoothManager()
    }

    func scanTableViewRefresh() {
        scanTableView.reloadData()
        tableViewScanTime--

        if tableViewScanTime <= 0 {
            timer1.invalidate()
            bluetoothManager!.CBmanager.stopScan()
            print("StopScan")
            tableViewScanTime = 5
            bluetoothManager!.peripheralArray.sortInPlace({$0.RSSI < $1.RSSI})
            self.refreshControl!.endRefreshing()
            self.scanTableView.userInteractionEnabled = true
        }
    }
}

//  BluetoothManager.swift

import UIKit
import CoreBluetooth

class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {

    struct BluetoothPeripheral {
        let name: String
        let UUID: String
        let RSSI: String
        let peripheral: CBPeripheral

        init(name: String, UUID: String, RSSI: NSNumber, peripheral: CBPeripheral) {
            self.name = "\(name)"
            self.UUID = "\(UUID)"
            self.RSSI = "\(RSSI)"
            self.peripheral = peripheral
        }
    }

    let DEVICE_NAME:String! = "TEST"

    //Creat an instance of ScanTableView Class
    var scanTableView: ScanTableView()


    var peripheralArray: [BluetoothPeripheral] = []
    var selectedPeripheral: BluetoothPeripheral?
    var characteristicArray: [CBCharacteristic] = []
    var CBmanager: CBCentralManager = CBCentralManager()
    var measurementValue: [[AnyObject?]] = [[]]

    //Basic functions
    override init() {
        super.init()
        CBmanager = CBCentralManager(delegate: self, queue: nil)
    }

    func connectPeripheral(selectedPeripheral: BluetoothPeripheral) {
        CBmanager.connectPeripheral(selectedPeripheral.peripheral, options: nil)
    }

    func disconnectPeripheral(selectedPeripheral: BluetoothPeripheral) {
        for characteristic in characteristicArray {
            selectedPeripheral.peripheral.setNotifyValue(false, forCharacteristic: characteristic as CBCharacteristic)
        }
        CBmanager.cancelPeripheralConnection(selectedPeripheral.peripheral)
    }

    func ScanForPeripherals() {
        CBmanager.scanForPeripheralsWithServices(nil, options: nil)
        print("Scanning")
    }

    func centralManagerDidUpdateState(central: CBCentralManager) {
        switch(central.state) {
        case .PoweredOn:
            CBmanager.scanForPeripheralsWithServices(nil, options: nil)
            print("scan")
        case .PoweredOff, .Resetting, .Unauthorized, .Unsupported, .Unknown:
            peripheralArray.removeAll()

            //This invokes an exception
            //scanTableView.scanTableView.reloadData()

            print("NO BLE!")
        }
    }

    func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
        let UUID = "\(peripheral.identifier)".substringFromIndex("\(peripheral.identifier)".startIndex.advancedBy(31))
        if let peripheralName = peripheral.name {
            if peripheralName.containsString(DEVICE_NAME) {
                peripheralArray.append(BluetoothPeripheral(name: peripheral.name!, UUID: UUID, RSSI: RSSI, peripheral: peripheral))
                print(peripheralArray)
            }
        }
    }

    func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
        print("Connected")
        measurementValue.removeAll()
        peripheral.delegate = self
        selectedPeripheral!.peripheral.discoverServices(nil)
    }

    func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
        print("Fail")
    }

    func centralManager(central: CBCentralManager, willRestoreState dict: [String : AnyObject]) {
        print("Restore")
    }

    func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
        print("Disconnected")
    }

    func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
        for service in peripheral.services! {
            peripheral.discoverCharacteristics(nil, forService: service)
        }
    }

    func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
        for characteristic in service.characteristics as [CBCharacteristic]!{
            if characteristic.properties.contains(CBCharacteristicProperties.Notify) {
                peripheral.discoverDescriptorsForCharacteristic(characteristic)
                peripheral.setNotifyValue(true, forCharacteristic: characteristic)
            }
        }
    }

    func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
        if characteristic.isNotifying {
            characteristicArray.append(characteristic as CBCharacteristic)
            peripheral.readValueForCharacteristic(characteristic)
        }
    }

    func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
        //Store new characteristic values
    }
}

现在我的问题:

显示的代码有效,但我无法在两个类之间进行交互。 例如,我想从我的 BluetoothManager 类重新加载我打开的 ScanTableView。那是不可能的......每次我尝试这个时,我都会遇到一个异常,我会打开一个可选的。为什么? “普通”类和 GUI 中显示的类(UITableView、UIView...)之间有什么区别吗?我记录了异常行...

如果有人能解释我在这种情况下该怎么做,那就太好了:)。

我很高兴有任何建议或改进!

【问题讨论】:

  • 你能指出哪一行给你的错误?与其使用 NSTimer,不如将表视图控制器设置为蓝牙管理器的委托并创建一个协议,以便蓝牙管理器可以就新发现的设备向表视图控制器提供建议,或者让蓝牙管理器使用 NSNotification 来提供建议对变化感兴趣的课程
  • 在第二课的中间,我注释掉了'scanTableView.scanTableView.reloadData()'。这部分调用异常。我想使用 NSTimer 模块来“控制”我搜索任何设备的时间。所以在这种情况下,我只搜索 5 秒。我不想使用 NSNotification 模块,以避免混淆代码......我想,我的 ScanTableView 的正确实例有问题。我用 'storyboard.instantiateViewControllerWithIdentifier("scanTableViewIdentifier") 试过了! ScanTableView' 但没有任何成功。
  • 如果你不想使用 NSNotification 那么你需要使用协议和委托。您的蓝牙管理器无法实例化 ScanTableView 的新实例,它需要与已经存在的实例进行通信,这将通过该视图控制器将自己设置为蓝牙管理器的委托
  • 好的,我会尽我所能来实现它并发布我的问题或解决方案:)!

标签: swift uitableview class bluetooth-lowenergy instance-variables


【解决方案1】:

就像@paulw11 所说,我必须创建一个委托协议:

protocol BluetoothDelegate: class {

    func ReloadView()
}

这个“ReloadView”方法是在我的 ScanTableView 类中声明的:

func ReloadView() {
    scanTableView.reloadData()
}

现在,我必须做一些额外的事情:

  1. 将 BluetoothDelegate 添加到 ScanTableView 类
  2. 声明一个变量'weak var delegate: BluetoothDelegate?'在 BluetoothManager 类中
  3. 在 BluetoothManager 类中的希望点使用“delegate?.ReloadView()”调用委托方法
  4. 在 ScanTableView 中使用“bluetoothManager?.delegate = self”激活委托

就是这样!

【讨论】:

    猜你喜欢
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-18
    • 1970-01-01
    • 2021-08-01
    • 2021-02-18
    • 1970-01-01
    相关资源
    最近更新 更多