小米手環(huán)iOS開發(fā)實戰(zhàn)(二):開發(fā)Demo讓你的手環(huán)振動起來
上一節(jié)講了CoreBluetooth的使用,理論知識很枯燥膛堤,那么現(xiàn)在先利用上一節(jié)講的內(nèi)容垮媒,做一個簡易手環(huán)應(yīng)用倔韭,實現(xiàn)連接/斷開手環(huán)恳谎,查看手環(huán)UUID、查看電量信息憋肖,并讓振動的功能因痛。
本節(jié)知識默認(rèn)大家掌握iOS的基礎(chǔ)控件,掌握通過storyboard或代碼搭建界面UI岸更,能夠利用Swift或Objective-C編寫程序鸵膏。文章會盡量詳細(xì)講解這些過程,當(dāng)然如果你是大旁醮叮可以放心跳讀谭企。
章節(jié)目錄
- 藍(lán)牙連接所涉及到的類
- 小米手環(huán)Demo應(yīng)用的開發(fā)
- 一些功能優(yōu)化
藍(lán)牙連接所涉及到的類
上一節(jié)講了怎么用CoreBluetooth,本節(jié)講一下所涉及到的類评肆,及常用的成員函數(shù)和成員變量债查,其他方法請見蘋果開發(fā)文檔。
CBCentralManager
此類為中心設(shè)備類瓜挽,用于控制作為中心設(shè)備時的行為
-
state
:獲取當(dāng)前中心設(shè)備狀態(tài) -
isScanning
:當(dāng)前中心設(shè)備是否在掃描外圍設(shè)備 -
stopScan()
:停止掃描外圍設(shè)備 -
scanForPeripherals(...)
:掃描外圍設(shè)備(請確保藍(lán)牙開啟) -
connect(...)
:連接外圍設(shè)備(需要先掃描到外圍設(shè)備) -
cancelPeripheralConnection(...)
:斷開外圍設(shè)備
CBPeripheral
此類為外圍設(shè)備類盹廷,用于對外圍設(shè)備進(jìn)行管理
-
name
:獲取外圍設(shè)備的名稱 -
rssi
:獲取當(dāng)前外圍設(shè)備的信號強度 -
state
:獲取外圍設(shè)備的狀態(tài)(disconnected/connecting/connected) - ★
services
:獲取外圍設(shè)備所提供的服務(wù)(需要先掃描到服務(wù)) -
discoverServices(...)
:掃描設(shè)備所提供的服務(wù) -
discoverCharacteristics(...)
:掃描特征值(需要先獲取服務(wù)) -
readValue(...)
:讀取特征值所對應(yīng)的值(需要先獲取到特征值,同時要注意此方法不反回值久橙,要用協(xié)議的didUpdateValueFor characteristic
方法處理)
是不是已經(jīng)懵了俄占?在此做一個圖大致描述一下流程,其實這些方法的調(diào)用還是很有規(guī)律的淆衷。
CBCharacteristic
外圍設(shè)備服務(wù)的特征值
- ★
Value
:獲取特征值對應(yīng)的值
</br>
小米手環(huán)Demo應(yīng)用的開發(fā)
本Demo是對上一節(jié)所講CoreBluetooth的操作復(fù)習(xí)缸榄,每個方法的實現(xiàn)已經(jīng)有所解釋,故在此不再贅述祝拯。如果有疑問甚带,歡迎在評論區(qū)提問及討論。
該Demo所要實現(xiàn)的功能:練習(xí)連接設(shè)備鹿驼、斷開設(shè)備欲低、讀取手環(huán)信息、讓手環(huán)振動畜晰。具體涉及到的知識點為連接和斷開設(shè)備砾莱、獲取設(shè)備服務(wù)和特征值、獲取特征值對應(yīng)的信息以及對其寫入凄鼻。
-
界面搭建
方便起見腊瑟,該項目直接采用storyboard搭建聚假,如果不會可以看項目Demo
界面搭建
@IBOutlet weak var scanButton: UIButton!
@IBOutlet weak var stopButton: UIButton!
@IBOutlet weak var vibrateButton: UIButton!
@IBOutlet weak var stopVibrateButton: UIButton!
@IBOutlet weak var loadingInd: UIActivityIndicatorView!
@IBOutlet weak var statusLabel: UILabel!
@IBOutlet weak var resultField: UITextView!
@IBOutlet weak var vibrateLevel: UISegmentedControl!
-
設(shè)置藍(lán)牙操作過程所需對象
涉及到的類在第一講已經(jīng)講解,如果有不明白的闰非,可以查閱前面的講解膘格。
var theManager: CBCentralManager!
var thePerpher: CBPeripheral!
var theVibrator: CBCharacteristic!
-
CoreBluetooth協(xié)議方法的實現(xiàn)
本部分內(nèi)容在第一講已經(jīng)涉及,如果有不明白的财松,可以查閱前面的講解瘪贱。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
theManager = CBCentralManager.init(delegate: self as? CBCentralManagerDelegate, queue: nil)
self.scanButton.isEnabled = false
statusLabel.text = ""
loadingInd.isHidden = true
}
// 掃描并連接
@IBAction func startConnectAction(_ sender: UIButton) {
switch theManager.state {
case .poweredOn:
statusLabel.text = "正在掃描…"
theManager.scanForPeripherals(withServices: nil, options: nil)
self.loadingInd.startAnimating()
self.scanButton.isEnabled = false
self.isDisconnected = false
default:
break
}
}
@IBAction func disconnectAction(_ sender: UIButton) {
if ((thePerpher) != nil) {
theManager.cancelPeripheralConnection(thePerpher)
thePerpher = nil
theVibrator = nil
statusLabel.text = "設(shè)備已斷開"
scanButton.isEnabled = true
isDisconnected = true
isVibrating = false
}
}
@IBAction func vibrateAction(_ sender: Any) {
if ((thePerpher != nil) && (theVibrator != nil)) {
let data: [UInt8] = [UInt8.init(vibrateLevel.selectedSegmentIndex+1)];
let theData: Data = Data.init(bytes: data)
thePerpher.writeValue(theData, for: theVibrator, type: CBCharacteristicWriteType.withoutResponse)
}
}
@IBAction func stopVibrateAction(_ sender: UIButton) {
if ((thePerpher != nil) && (theVibrator != nil)) {
let data: [UInt8] = [UInt8.init(0)];
let theData: Data = Data.init(bytes: data)
thePerpher.writeValue(theData, for: theVibrator, type: CBCharacteristicWriteType.withoutResponse)
isVibrating = false
}
}
// 處理當(dāng)前藍(lán)牙主設(shè)備狀態(tài)
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
statusLabel.text = "藍(lán)牙已開啟"
self.scanButton.isEnabled = true
default:
statusLabel.text = "藍(lán)牙未開啟!"
self.loadingInd.stopAnimating()
}
}
// 掃描到設(shè)備
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if (peripheral.name?.hasSuffix("MI"))! {
thePerpher = peripheral
central.stopScan()
central.connect(peripheral, options: nil)
statusLabel.text = "搜索成功辆毡,開始連接"
}
// 特征值匹配請用 peripheral.identifier.uuidString
resultField.text = String.init(format: "發(fā)現(xiàn)手環(huán)\n名稱:%@\nUUID:%@\n", peripheral.name!, peripheral.identifier.uuidString)
}
// 成功連接到設(shè)備
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
statusLabel.text = "連接成功菜秦,正在掃描信息..."
peripheral.delegate = self
peripheral.discoverServices(nil)
}
// 連接到設(shè)備失敗
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
loadingInd.stopAnimating()
statusLabel.text = "連接設(shè)備失敗"
scanButton.isEnabled = true
}
// 掃描服務(wù)
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if ((error) != nil) {
statusLabel.text = "查找服務(wù)失敗"
loadingInd.stopAnimating()
scanButton.isEnabled = true
return
}
else {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
// 掃描到特征值
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if ((error) != nil) {
statusLabel.text = "查找服務(wù)失敗"
loadingInd.stopAnimating()
scanButton.isEnabled = true
return
}
else {
for characteristic in service.characteristics! {
peripheral.setNotifyValue(true, for: characteristic)
if (characteristic.uuid.uuidString == BATTERY) {
peripheral.readValue(for: characteristic)
}
else if (characteristic.uuid.uuidString == DEVICE) {
peripheral.readValue(for: characteristic)
}
else if (characteristic.uuid.uuidString == VIBRATE) {
theVibrator = characteristic
}
}
}
}
// 掃描到具體設(shè)備
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if ((error) != nil) {
statusLabel.text = "從設(shè)備獲取值失敗"
return
}
else {
if(characteristic.uuid.uuidString == BATTERY) {
var batteryBytes = [UInt8](characteristic.value!)
var batteryVal:Int = Int.init(batteryBytes[0])
self.resultField.text = String.init(format: "%@電量:%d%%\n", resultField.text, batteryVal)
}
loadingInd.stopAnimating()
scanButton.isEnabled = true
statusLabel.text = "信息掃描完成!"
if (isVibrating) {
vibrateAction(Any)
}
}
}
// 與設(shè)備斷開連接
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
statusLabel.text = "設(shè)備已斷開"
scanButton.isEnabled = true
if(!isDisconnected) {
theManager.scanForPeripherals(withServices: nil, options: nil)
}
}
再次重提一下我在解決關(guān)于CBCentralManager的State屬性遇到的問題:
CBCentralManager的State屬性在之前是CBCentralManagerState舶掖,但是現(xiàn)在變成了CBManagerState球昨,而后者需要iOS10以上才支持。查了StackoverFlow發(fā)現(xiàn)很多人也遇到了同樣的問題眨攘,也是蘋果很矛盾的一個用發(fā)主慰。通過測試發(fā)現(xiàn)用switch語句對state屬性判斷可以解決系統(tǒng)版本限制的問題,也是普遍采用的方法鲫售。
補充:
小米手環(huán)振動的UUID是2A06共螺,0代表不振,1為短振龟虎,2為長振璃谨。
其他UUID也均有相關(guān)文章有寫,太多就不一一列舉鲤妥,可以直接Google之佳吞。如果需要的人比較多,我可以稍后撰寫一份對照表棉安。
接下來底扳,部署->調(diào)試即可。功能運行正常贡耽。
</br>
一些功能改進(jìn)
前一部分改進(jìn)已經(jīng)放到了上述代碼中衷模,若后期有改進(jìn)將更新此處。
</br>
至此已經(jīng)完成了對第一講知識的復(fù)習(xí)蒲赂,接下來我們將講解對小米手環(huán)其他功能的開發(fā)阱冶。最終截稿時完成仿小米手環(huán)APP,并實現(xiàn)各種創(chuàng)意功能滥嘴。
PS:現(xiàn)在開發(fā)小米手環(huán)可能都是出于情懷了吧木蹬?還有沒有必要繼續(xù)做下去呢。如果想要二次開發(fā)的人比較多若皱,可以嘗試做一套SDK方便開發(fā)镊叁。
寫文章不易尘颓,如果覺得滿意,歡迎大家粉一下我的GitHub晦譬,以及動動手指Star一下我的項目疤苹,持續(xù)更新需要你的支持!
本人GitHub:https://github.com/Minecodecraft
本項目鏈接:https://github.com/Minecodecraft/MiBandDemo
“小米手環(huán)iOS開發(fā)實戰(zhàn)”系列
小米手環(huán)iOS開發(fā)實戰(zhàn)(一):iOS藍(lán)牙框架CoreBluetooth
小米手環(huán)iOS開發(fā)實戰(zhàn)(二):開發(fā)Demo讓你的手環(huán)振動起來