扫描BLE设备并将其显示在UITableView中

时间:2016-02-13 11:12:47

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

我试图找到一种方法来扫描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 ...)之间是否存在任何差异?我记录了例外行......

如果有人能解释我在这种情况下该做什么,那将是非常好的:)。

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

1 个答案:

答案 0 :(得分: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. 使用'bluetoothManager?.delegate = self'
  5. 在ScanTableView中激活委托

    就是这样!