iOS - 顯示藍芽搜尋裝置

此篇主要是如何顯示藍芽裝置

結合之前搜尋藍芽裝置TableView分為兩個群組的文章做個整合.

Step 1: 建立BtDelegate protocol

BtDelegate 主要是將搜尋到的藍芽裝置, 傳回給上層UI

1
2
3
4
5
import Foundation

protocol BtDelegate {
func findBtDevice(name: String?)
}

Step 2: BtManager 搜尋到裝置後, 通知上層UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BtManager : NSObject {
var mCBCentralManager: CBCentralManager!

//callback manager
var delegate : BtDelegate?

func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Device: \(peripheral.name) : \(peripheral.identifier.UUIDString)");

if (!isAlreadyFound(peripheral.identifier.UUIDString)) {
insertFoundDevice(peripheral.identifier.UUIDString, newName: peripheral.name);
delegate!.findBtDevice(peripheral.name);
} else {
print("This Device is discovered: \(peripheral.identifier.UUIDString)");
}
}

Step 3: ViewController 繼承 BtDelegate, 並設定給 BtManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ViewController: UIViewController {
var mBtManager : BtManager!;

override func viewDidLoad() {
super.viewDidLoad()
mBtManager = BtManager();
mBtManager.delegate = self;
}

extension ViewController: BtDelegate {
func findBtDevice(name: String?) {
print("Find Device: \(name)");
}
}
}

Step 4: 顯示於TableView 上

UITableView 與程式建立連結

監聽TableView Data Change

1
mDeviceTableView.dataSource = self;

插入資料到TableViewCell 中

在此例子中, 我們將新資料插到最後一排.
首先先在 BtManage.swift 中取得目前搜尋到裝置的個數

1
2
3
4
5
6
class BtManager : NSObject {
var mFoundDevices : Dictionary<String , BtDevice>!;
func getFoundDevicesSize() -> Int {
return mFoundDevices.count;
}
}

插入資料 (由於搜尋到的裝置我們放在Group 1 , 所以 inSection : 0)

1
2
3
4
5
6
7
8
9
extension ViewController: BtDelegate {
func findBtDevice(name: String?) {
print("Find Device: \(name)");

let indexPath = NSIndexPath(
forRow: mBtManager.getFoundDevicesSize()-1, inSection: 0);
mDeviceTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic);
}
}

設定目前Row個數

1
2
3
4
5
6
7
8
9
10
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (section) {
case 0:
return mBtManager.getFoundDevicesSize();
case 1:
return 0;
default:
return 0;
}
}

設定TableViewCell內容

首先在BtManager.swift中, 取得BT Device Name

1
2
3
4
5
6
7
8
9
func getFoundDeviceName(index: Int) -> String? {
print("index: \(index)");
let keyIndex = mFoundDevices.startIndex.advancedBy(index);
print("keyIndex: \(keyIndex)");
let key = mFoundDevices.keys[keyIndex];
print("key: \(key)");
let btDevice = mFoundDevices[key]!;
return btDevice.name;
}

設定內容

1
2
3
4
5
6
7
8
9
10
11
12
13
func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var deviceName : String?;
switch(indexPath.section) {
case 0:
deviceName = mBtManager.getFoundDeviceName(indexPath.row);
default:
deviceName = "Unknow";
}

let cell: UITableViewCell = UITableViewCell( style:UITableViewCellStyle.Subtitle, reuseIdentifier: "deviceCell");
cell.textLabel?.text = "Device Name: \(deviceName)";
return cell;
}

Step 5: 重新搜尋時, 必須清空TableView內容

1
2
3
4
5
6
7
8
9
10
func startScan() {
mLoadingView.startAnimating();
mBtManager.startToScan();
cleanAllFoundDevices();
changeToStopButton();
}

private func cleanAllFoundDevices() {
mDeviceTableView.reloadData();
}

Source Code

BtDevice class 的定義可參考
iOS - 搜尋藍芽裝置 Part II - 避免搜尋到重複的裝置

BtManager.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import Foundation
import CoreBluetooth

class BtManager : NSObject {
var mCBCentralManager: CBCentralManager!

//callback manager
var delegate : BtDelegate?
var mFoundDevices : Dictionary<String , BtDevice>!;

override init() {
super.init();
initCBCentralManager();
mFoundDevices = [String : BtDevice]();
}

internal func initCBCentralManager() {
mCBCentralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true]);
}

func isScanning() -> Bool {
return mCBCentralManager.isScanning;
}

func startToScan() {
if (mCBCentralManager.state == CBCentralManagerState.PoweredOff) {
print("state is off");
initCBCentralManager();
} else {
if (isScanning()) {
print("state is scanning");
} else {
clearAllFoundDevices();
mCBCentralManager.scanForPeripheralsWithServices(nil, options: nil);
print("start to scan: \(isScanning())");
}
}
}

func stopScanning() {
mCBCentralManager.stopScan();
}

func getFoundDevicesSize() -> Int {
return mFoundDevices.count;
}

func getFoundDeviceName(index: Int) -> String? {
print("index: \(index)");
let keyIndex = mFoundDevices.startIndex.advancedBy(index);
print("keyIndex: \(keyIndex)");
let key = mFoundDevices.keys[keyIndex];
print("key: \(key)");
let btDevice = mFoundDevices[key]!;
return btDevice.name;
}
}

extension BtManager : CBCentralManagerDelegate {
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state) {
case CBCentralManagerState.PoweredOn:
print("state On");
case CBCentralManagerState.PoweredOff:
print("state Off");
case CBCentralManagerState.Unknown:
fallthrough;
default:
print("state Unknow");
}
}

func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Device: \(peripheral.name) : \(peripheral.identifier.UUIDString)");

if (!isAlreadyFound(peripheral.identifier.UUIDString)) {
insertFoundDevice(peripheral.identifier.UUIDString, newName: peripheral.name);
delegate!.findBtDevice(peripheral.name);
} else {
print("This Device is discovered: \(peripheral.identifier.UUIDString)");
}
}

internal func isAlreadyFound(uuid : String!) -> Bool {
return mFoundDevices[uuid] != nil;
}

internal func insertFoundDevice(newUuid: String!, newName: String?) {
let btDevice = BtDevice(uuid: newUuid, name: newName);
mFoundDevices[newUuid] = btDevice;
}

internal func clearAllFoundDevices() {
mFoundDevices.removeAll();
}
}
ViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import UIKit

class ViewController: UIViewController {
var mBtManager : BtManager!;
@IBOutlet weak var mLoadingView: UIActivityIndicatorView!
@IBOutlet weak var mDeviceTableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()
mBtManager = BtManager();
mBtManager.delegate = self;
mLoadingView.stopAnimating();
mLoadingView.hidesWhenStopped = true;
mDeviceTableView.dataSource = self;
}

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

@IBAction func prepareScan(sender: AnyObject) {
startScan();
}

func startScan() {
mLoadingView.startAnimating();
mBtManager.startToScan();
cleanAllFoundDevices();
changeToStopButton();
}

private func cleanAllFoundDevices() {
mDeviceTableView.reloadData();
}

func stopScan() {
mLoadingView.stopAnimating();
mBtManager.stopScanning();
changeToScanButton();
}

private func changeToStopButton() {
let stopScanningButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: #selector(ViewController.stopScan));
self.navigationItem.rightBarButtonItem = stopScanningButton;
}

private func changeToScanButton() {
let scanButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Search, target: self, action: #selector(ViewController.startScan));
self.navigationItem.rightBarButtonItem = scanButton;
}
}

extension ViewController : UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2;
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch (section) {
case 0:
return "Found Devices:";
case 1:
return "Connected Devices:";
default:
return "Unknow";
}
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (section) {
case 0:
return mBtManager.getFoundDevicesSize();
case 1:
return 0;
default:
return 0;
}
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var deviceName : String?;
switch(indexPath.section) {
case 0:
deviceName = mBtManager.getFoundDeviceName(indexPath.row);
default:
deviceName = "Unknow";
}

let cell: UITableViewCell = UITableViewCell( style:UITableViewCellStyle.Subtitle, reuseIdentifier: "deviceCell");

cell.textLabel?.text = "Device Name: \(deviceName)";
return cell;
}
}

extension ViewController: BtDelegate {
func findBtDevice(name: String?) {
print("Find Device: \(name)");

let indexPath = NSIndexPath(forRow: mBtManager.getFoundDevicesSize()-1, inSection: 0);

mDeviceTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic);
}
}
作者

Nick Lin

發表於

2016-08-25

更新於

2023-01-18

許可協議


評論