iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Mobile Development

我將成為Swift之強者系列 第 28

Day28 - iOS 藍牙開發實作:從掃描到 UI 呈現,打造藍牙設備清單

  • 分享至 

  • xImage
  •  

Day28 - iOS 藍牙開發實作:從掃描到 UI 呈現,打造藍牙設備清單

昨天我們完成了 BluetoothService 的藍牙核心邏輯,讓 App 可以進行掃描、連線並接收藍牙數據。
今天要更進一步,把這些功能與畫面串接起來,讓使用者能清楚地看到附近的藍牙裝置,並能點擊連接設備。


從 Service 到 UI 的橋樑:Delegate

在前一篇中,我們在 BluetoothService 定義了一個協議:

protocol BluetoothServiceDelegate: AnyObject {
    func getBLEPeripherals(peripherals: [CBPeripheral])   // 回傳掃描到的裝置
    func getBLEPeripheralsValue(value: String)            // 回傳接收到的數據
}

這個 delegate藍牙核心與畫面之間的橋樑
當 Service 掃描到新裝置或接收到資料時,會透過這個協議回傳給 UI。
而今天的 MainViewController 就會負責實作這個協議,讓畫面能即時反映藍牙狀態。


MainViewController:藍牙設備清單與即時數據

先來看看控制器的架構:

class MainViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var lbLightNumber: UILabel!
    
    private var peripherals: [CBPeripheral] = []
    private var connectedPeripheral: CBPeripheral?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setUI()
        BluetoothService.shared.delegate = self   // 關鍵:設定代理
    }
}

這裡主要做了兩件事:

  1. 建立 peripherals 陣列,存放掃描到的藍牙裝置。
  2. viewDidLoad 裡設定代理 (delegate),確保藍牙事件能即時傳回 UI。

顯示藍牙設備列表:UITableView

接著,使用 UITableView 來顯示掃描到的藍牙清單:

extension MainViewController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return peripherals.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "BluetoothTableViewCell", for: indexPath) as? BluetoothTableViewCell else {
            return UITableViewCell()
        }
        
        let peripheral = peripherals[indexPath.row]
        cell.lbName.text = peripheral.name ?? "未知設備"
        return cell
    }
}

這樣就能在畫面上即時顯示掃描到的藍牙裝置名稱。
當使用者點擊其中一個項目時:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    let selectedPeripheral = peripherals[indexPath.row]
    BluetoothService.shared.connectPeripheral(peripheral: selectedPeripheral)
}

App 會呼叫 BluetoothService 去嘗試連線該設備。


即時接收藍牙資料

接著實作 BluetoothServiceDelegate,處理掃描與資料更新事件:

extension MainViewController: BluetoothServiceDelegate {
    
    func getBLEPeripherals(peripherals: [CBPeripheral]) {
        self.peripherals = peripherals
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
    
    func getBLEPeripheralsValue(value: String) {
        DispatchQueue.main.async {
            self.lbLightNumber.text = "光線強度:\(value)"
        }
    }
}

這裡有兩個關鍵重點:

  1. UI 更新一定要在主線程執行 (DispatchQueue.main.async)
    因為藍牙事件多在背景執行緒觸發,若直接更新 UI 會造成閃退或異常。
  2. getBLEPeripheralsValue 即時顯示裝置傳來的數據,例如感測器的光線值或其他量測資訊。

今天的成果

今天我們完成了整個「藍牙掃描 → UI 顯示 → 連線 → 數據更新」的流程:

  • 掃描藍牙裝置
  • 動態顯示在畫面上
  • 點擊裝置後自動連線
  • 即時接收並顯示感測數據

到這一步,我們的藍牙 App 已經具備完整的互動性與基本資料傳輸能力 🎉


明天預告

明天(Day29),我們將深入探討 藍牙數據傳輸與解析機制,包含:

  • 如何解析多格式藍牙資料
  • 避免重複連線與重複通知
  • 強化連線穩定度與 UI 響應

上一篇
Day27 - iOS 藍芽開發實作:掃描裝置、連線與接收資料
下一篇
Day29 - iOS 藍牙開發實作:總結篇
系列文
我將成為Swift之強者30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言