你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 實例教程:快速上手iOS iBeacon開發(Swift版)

實例教程:快速上手iOS iBeacon開發(Swift版)

編輯:IOS開發基礎

本文由CocoaChina譯者培子翻譯自Raywenderlich
原文:iBeacons Tutorial with iOS and Swift


iBeacons-250x250.png備注:該教程針是Adrian Strahan針對iOS 8,swift1.2,以及Xcode6.3而更新的。原始文章由Tutorial Team成員Chris Wagner供稿。

你曾經想過用手機在一個大型建築物中為自己定位嗎,比如購物中心,或者棒球場。

當然,GPS可以讓你得知自己身處哪一座建築物裡。但是如果想要在這些鋼筋混凝土堆砌而成的建築中獲得精確的GPS信號,只能祝你好運了。你所需要的是內置在建築物中一些設備,(通過它們)讓手機獲取確定你的位置。

iBeacon,我們來了!在這篇iBeacons教程中,將會開發一個App,功能包括,紀錄已知的iBeacon發射器;還有當你的手機設備移出它的信號輻射范圍時,App會提醒你。這個App的一大用處是:將iBeacon發射器放置在你的電腦包、錢包裡,甚至綁在貓咪的項圈上,以及其他貴重物品上等。一旦設備移出iBeacon輻射范圍,App就會檢測到並且通知你。

如果想繼續深入,你需要一台iOS設備和一個iBeacon設備。如果沒有iBeacon設備,但還有另外一個iOS設備,你也可以把它當作iBeacon設備來用。

開始吧


目前有很多可用的iBeacon設備,在Google一搜一大堆。但是蘋果公司引進iBeacon時,他們還聲稱任何兼容iOS的設備都能充當iBeacon。目前包括以下設備:

  • iPhone4s 或者 之後的iPhone設備

  • 第三代iPad或者 之後iPad設備

  • iPad mini 或者 之後iPad mini設備

  • 第五代iPod touch 或者之後iPod touch設備

備注:如果沒有單獨的iBeacon發射器,但擁有一台支持iBeacons的iOS設備,你可以對照《what's new in core location of iOS 7 by Tutorials》書中第22章的描述,開發一個充當iBeacon角色的App。

iBeacon就是一種低功耗藍牙設備,它能以特定的數據結構廣播數據信息。這些屬性雖然超出了本篇教程的討論范圍,但是對理解iOS能夠監測iBeacon設備很重要,這些設備廣播三個數據:UUID,major和minor。UUID是universally unique identifier的首字母縮寫,它是一個128位的值,通常以十六進制的方式顯示:B558CBDA-4472-4211-A350-FF1196FFE8C8

在iBeacon的環境下,UUID用來表示設備的最高級別標識。

Major和minor值則是在UUID下提供更細微的辨別標識。它們的值用16位無符號整型數簡單表示,用來識別單個iBeacon設備,即使這些設備有著相同的UUID。

例如,你有多家百貨公司,打算讓所有的iBeacon設備發射相同的UUID信息,但是每家百貨公司擁有各自的major值,並且在每家百貨公司的各部門又擁有各自的minor值。這樣App就可以對放置在邁阿密的弗羅裡達分店的鞋業部門的iBeacon設備做相應的信息反饋。

ForgetMeNot 開始項目


從這兒下載工程啟動文件,它的界面很簡單,就是在表格視圖中添加和刪除對象。表格裡的每一個對象代表一個iBeacon設備,在現實生活中,它可以代表你不想遺失的東西。

啟動並構建App,你會看到一個空的列表,沒有任何對象。點擊+按鈕,為它添加新項,如下:

firstlaunch-281x500.png

首屏

添加新項,你需要為新對象命名,還有與之對應的值。可以通過查看iBeacon的文檔來獲得它的UUID。立即添加進去吧,或者用一些占位符值,如下:

additem-281x500.png
添加項目

點擊save按鈕,返回到列表界面,就會看到location顯示為Unknown的對象,如下:

itemadded-281x500.png
添加的項目列表

想添加多少,就添加多少,或者刪掉已有的。NSUserDefaults會保存列表裡的選項,以便再次打開App時可用。

界面上,看上去也就那麼回事;最有趣的部分藏在了表象之下。該App的獨特之處就在於表格裡顯示的Item類。

在Xcode中打開Item.swift。該類對應著界面從用戶那裡請求的信息,它遵循NSCoding協議,因此可以被串行和並行的存儲到硬盤上。

現在看一下AddItemViewController.swift。這是用來添加新Item對象的視圖控制器。除了對用戶輸入做了些驗證,確保用戶輸入有效的名稱和UUID之外,它就是一個簡單的UITableViewController。

一旦nameTextField和uuidTextField內容有效,右上角的Save按鈕就會變為可點擊狀態了。

既然已經熟悉了項目啟動文件,你就能夠在你的工程中實現iBeacon了。

Core Location 許可


你的設備當然不會自動監測iBeacon的,所以首先你得告知它。CLBeaconRegion類代表一個iBeacon;CL前綴的類表示它屬於Core Location框架。

iBeacon與Core Location關聯在一起看上去有點奇怪,因為它就是一個藍牙設備而已,但是也可以這麼認為,那就是iBeacon提供小范圍定位功能,而GPS提供的是大范圍定位功能。當想讓iOS設備充當iBeacon時,你還需要引入Core Bluetooth框架,但只想檢測iBeacon設備,你只需要Core Location就行了。

首先為Item引入CLBeaconRegion。

打開Item.swift,在頂部添加如下代碼:

import CoreLocation

接下來,更新majorValue和minorValue定義,並初始化如下:

let majorValue: CLBeaconMajorValue
let minorValue: CLBeaconMinorValue
 
init(name: String, uuid: NSUUID, majorValue: CLBeaconMajorValue, minorValue: CLBeaconMinorValue) {
  self.name = name
  self.uuid = uuid
  self.majorValue = majorValue
  self.minorValue = minorValue
}

CLBeaconMajorValue?和?CLBeaconMinorValue都是UInt16型,用來表示major和minor值。

雖然它們的數據類型一樣,但是為了提高Item的可讀性和增加數據的安全性,你最好不要把major和minor值搞混。

打開ItemsViewController.swift,在頂部引入Core Location:

import CoreLocation

為其添加如下屬性:

let locationManager = CLLocationManager()

當引入Core Location功能時,需要用到這個CLLocationManager對象。

然後,更新viewDidLoad(),如下:

override func viewDidLoad() {
  super.viewDidLoad()
 
  locationManager.requestAlwaysAuthorization()
 
  loadItems()
}

如果設備沒有給App授權,它就會調用requestAlwaysAuthorization()方法來提示用戶是否允許使用定位服務。在iOS8中,Always和When in Use是最新有關定位授權的狀態。在App使用Always權限授權時,只要app在前台或者後台處於運行狀態,它就可以啟動所有可用的定位服務。

因為該教程對iBeacon一直進行區域監測,所以當app處於前台或者後台運行時,需要Always定位許可來觸發區域事件。

iOS8要求你在Info.plist?中設置一串字符,該字符串會在app請求定位服務時顯示出來。如果不設置它,定位服務就會無效,甚至都得不到任何警告!

打開Info.plist,選中Information Property List 後,點擊+,添加新的一行。

plistwithoutentry.png

遺憾的是,需要添加的key不是在下拉列表中預定義好的,需要自己輸入進去。把key設置為NSLocationAlwaysUsageDescription,Type設為String類型。然後輸入提示文字,告訴用戶用戶為何要開啟定位服務,例如:"ForgetMeNot would like to teach you how to use iBeacons!"

plistwithentry.png

啟動並構建app,一旦運行,你會看到一段消息,詢問你是否允許app使用定位服務:

allowlocation-281x500.jpg

允許訪問位置

選擇Allow,app就能夠追蹤iBeacon設備了

監聽iBeacon


現在app有了定位服務的授權,是時候搜索beacon設備了!在ItemsViewController.swift的底部添加類extension,如下:

// MARK: - CLLocationManagerDelegate
 
extension ItemsViewController: CLLocationManagerDelegate {
}

這段代碼表示ItemsViewController遵循CLLocationManagerDelegate協議。接著在extension裡添加委托方法,讓它們結合在一起。

在viewDidLoad方法最後一行添加如下代碼:

locationManager.delegate = self

設置CLLocationManager的委托對象為self,以便接收委托方法的回調。

有了CLLocationManager對象,你可以指導app使用CLBeaconRegion對一些指定的區域開啟監測。當注冊了一個監控區域,之後只要啟動app,這些區域就會存在下去。當你對一個交叉區域的邊界作出反應,而app沒有運行時,這點很重要。

列表中的iBeacon對象由Item類的items數組屬性表示。然而CLLocationManager希望你提供一個CLBeaconRegion對象來開啟監控。

在ItemsViewController.swift中創建如下輔助方法:

func beaconRegionWithItem(item:Item) -> CLBeaconRegion {
  let beaconRegion = CLBeaconRegion(proximityUUID: item.uuid,
                                            major: item.majorValue,
                                            minor: item.minorValue,
                                       identifier: item.name)
  return beaconRegion
}

該方法通過提供的Item,返回一個CLBeaconRegion對象。

可以看出CLLBeaconRegion和Item之間有相似的數據結構,所以生成CLBeaconRegion對象很簡單,因為它有直接對應的屬性UUID,major值和minor值

現在創建一個方法來監控已有的Item對象,給ItemsViewController添加如下代碼:

func startMonitoringItem(item: Item) {
  let beaconRegion = beaconRegionWithItem(item)
  locationManager.startMonitoringForRegion(beaconRegion)
  locationManager.startRangingBeaconsInRegion(beaconRegion)
}

該方法使用一個Item參數,調用之前定義的方法生成CLBeaconRegion。然後讓location manager開始監控已有的區域,並在該區域內檢索iBeacon設備。

在給定的區域內,檢索就是發現iBeacon設備的過程,並確定iBeacon設備與iOS設備之間的距離。一台接收到iBeacon發射信息的iOS設備能估算出它與iBeacon之間的距離。這個距離呗劃分為三個區域范圍:

  • Immediate 幾厘米之內

  • Near 幾米之內

  • Far 10米開外

備注:Far,Near和Immediate對應的實際距離不是固定的,在Stack Overflow Question有提到它,並給它一個組略的距離范圍。

默認來講,無論app是否運行,只要你進入或者走出監控區域,app都會通知你。另一方面,檢索iBeacon設備這一過程只會在app處於運行狀態時才會監測出區域距離。

還需要在某個Item區域被刪除時,終止對它的監控。在ItemViewController添加如下代碼:

func stopMonitoringItem(item: Item) {
  let beaconRegion = beaconRegionWithItem(item)
  locationManager.stopMonitoringForRegion(beaconRegion)
  locationManager.stopRangingBeaconsInRegion(beaconRegion)
}

上述方法的作用剛好與startMonitoringItem方法相反,直到CLLocationManager終止監控和檢索活動。

現在,已經創建了開始和終止方法,是時候用它們了!開啟監控的正確時機是在用戶為列表添加新的item對象的時候。

看一下ItemsViewController中的saveItem(_:),該unwind segue轉場是在用戶點擊AddItemViewController的Save按鈕時觸發,它同時生成了一個監控區域。在這個方法中找到調用persistItems()的那行,在它之前的一行添加如下代碼:

startMonitoringItem(newItem)

當用戶保存一個item對象時,就會激活這個監控。同樣的,當啟動app時,app從NSUserDefaults加載已存的item對象,這也就意味著啟動的同時就需要開啟對應的區域監控。

在ItemsViewController .swift中,找到loadItems(),在for循環中添加如下代碼:

startMonitoringItem(item)

這會確保每個item區域都處於受監控狀態。

還有,你需要關注一下從列表中刪除item。找到tableView(_:commitEditingStyle:forRowAtIndexPath:),在itemToRemove之後添加如下代碼:

stopMonitoringItem(itemToRemove)

當用戶刪除表視圖某行時,就會調用這個委托方法。現有的代碼處理的是將該行對象從數據模型和視圖上刪除,剛剛加的代碼將會終止對item監控。

此時此刻,你已經完成了很多事情!app已經有開啟和終止對制定iBeacon的監控。

這個階段,可以啟動並構建app了;但是盡管已經注冊的iBeacon在你的app檢索范圍內,可是目前app無法對發現iBeacon設備作出任何反饋......還需要繼續完善它!

發現iBeacon的反饋


既然location manager已經監控iBeacon,是時候通過CLLocationManagerDelegate的某些方法對iBeacon作出反應。

首先也是最重要的是錯誤處理,因為你正在處理設備指定的硬件信息,想知道監控或者檢索失敗的任何可能原因。

給ItemsViewController.swift裡的CLLocationManagerDelegate的類extension添加下面兩個方法:

func locationManager(manager: CLLocationManager!, monitoringDidFailForRegion region: CLRegion!, withError error: NSError!) {
  println("Failed monitoring region: \(error.description)")
}
 
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
  println("Location manager failed: \(error.description)")
}

這些方法對監控iBeacon時接收的所有錯誤信息做一個簡單記錄。

如果app一切運行正常,你不會看到這些方法的輸出信息。然而,如果某些地方出了問題,很有可能從錯誤日志中獲得很有價值的信息。

下一步就是實時顯示iBeacon與iOS設備之間的距離。在CLLocationManagerDelegate的類extension裡,實現如下委托方法:

func locationManager(manager: CLLocationManager!, didRangeBeacons beacons: [AnyObject]!, inRegion region: CLBeaconRegion!) {
  if let beacons = beacons as? [CLBeacon] {
    for beacon in beacons {
      for item in items {
        // TODO: Determine if item is equal to ranged beacon
      }
    }
  }
}

這個委托方法會在iBeacon進入監控范圍,或者移除監控范圍,或者iBeacon輻射范圍發生改變的時候被調用。

該app的目的就是使用由上述委托方法提供的iBeacon數組來更新列表的item對象,並顯示它們與設備的感應距離。重復迭代beacons數組,之後再迭代items數組,查看是兩者之間是否有匹配的部分。稍後來處理TODO部分代碼。

打開item .swift,給Item類添加如下屬性:

dynamic var lastSeenBeacon: CLBeacon

該屬性存儲的是最後一個與之匹配的CLBeacon對象,用來顯示距離信息。它有一個dynamic修飾符,以便於稍後對它使用key-value observation。

在Item.swift底部,在類定義的外面,添加==操作符判斷如下代碼:

func ==(item: Item, beacon: CLBeacon) -> Bool {
  return ((beacon.proximityUUID.UUIDString == item.uuid.UUIDString)
    && (Int(beacon.major) == Int(item.majorValue))
    && (Int(beacon.minor) == Int(item.minorValue)))
}

該等號操作符函數比較CLBeacon與Item對象,檢查它們是否相等--即,它們所有的標識是否匹配。這種情形下,如果UUID,major和minor值全部相同,那麼CLBeacon與Item對象相等。

現在繼續完成檢索的委托方法,調用上述輔助方法。打開ItemsViewController.swift,回到locationManager(_:didRangeBeacons:inRegion:)。替換for循環裡的TODO部分,如下:

if item == beacon {
 item.lastSeenBeacon = beacon
}

這裡,當發現一個item與iBeacon匹配時,把它賦值給lastSeenBeacon。你會發現item和iBeacon受益於之前等號操作符函數!

是時候使用該屬性來顯示監測到的iBeacon設備與iOS設備之間的距離。

打開ItemCell.swift,在didSet屬性觀察者起始部位,添加如下代碼:

item?.addObserver(self, forKeyPath: "lastSeenBeacon", options: .New, context: nil)

當為cell設置item時,同樣要為lastSeenBeacon添加一個觀察者。為了保持平衡,還要在cell已經設置過了item時,刪除該觀察者。為didSet添加一個willSet屬性觀察者。確保它屬於item屬性:

willSet {
  if let thisItem = item {
    thisItem.removeObserver(self, forKeyPath: "lastSeenBeacon")
  }
}

這會確保只有一個item 對象被觀察。

當然,當cell被廢棄時,同樣需要刪除觀察者。還在ItemCell.swift,添加如下代碼:

deinit {
  item?.removeObserver(self, forKeyPath: "lastSeenBeacon")
}

既然正在觀測距離的變化,你就可以在iBeacon的距離發生變化的時候通過一些邏輯規則來作出反饋。

每個CLBeacon對象都有一個proximity屬性,它是一個包含Far,Near,Immediate和Unknown的枚舉。

在ItemCell.swift中,為Core Location添加導入的申明:

import?CoreLocation

下一步,為ItemCell添加如下代碼:

func nameForProximity(proximity: CLProximity) -> String {
  switch proximity {
  case .Unknown:
    return "Unknown"
  case .Immediate:
    return "Immediate"
  case .Near:
    return "Near"
  case .Far:
    return "Far"
  }
}

該發放返回一個易讀的遠近值,後面會用到它。

接著,添加如下代碼:

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer) {
  if let anItem = object as? Item where anItem == item && keyPath == "lastSeenBeacon" {
    let proximity = nameForProximity(anItem.lastSeenBeacon!.proximity)
    let accuracy = String(format: "%.2f", anItem.lastSeenBeacon!.accuracy)
    detailTextLabel!.text = "Location: \(proximity) (approx. \(accuracy)m)"
  }
}

每次lastSeenBeacon發生改變時,都會調用這個方法,它會用CLBeacon的proximity值和accuracy值設置cell的detailTextLabel.text屬性。

後面這個accuracy的值即使你的iOS設備和iBeacon沒有移動,由於無線電頻率的緣故,也會一直浮動,因此不要指望它來達到beacon的精確定位。

現在確保已經注冊了iBeacon,然後讓你的iOS設備逐漸靠近它,或者遠離它。當你移動時,你會看到標簽也會隨之更新,如下:

11.jpg

你會發現proximity和accuracy值受到iBeacon位置的影響比較劇烈;如果把它放在類似箱子,包之類的東西裡,信號就會受到阻礙,這是因為iBeacon是低功耗設備,它的信號很容易被減弱。

記住這點,在設計app時,需要把iBeacon放置在最妥善的位置上。

推送


app看上去已經很棒了;能顯示iBeacon設備,並且還能實時監控它們的距離。但是這還不是app的終極目標。當app沒有處於運行狀態時,用戶忽略了他們的手提包,或者寵物貓跑丟了--更有甚者,貓和手提包都不翼而飛了!

zorro-ibeacon-250x250.jpg

他們是不是好可憐?

此刻,你可能注意到為app添加iBeacon功能不需要太多代碼。當貓貓和手提包都不見了時,添加一個推送也一樣簡單!

打開AppDelegate .swift,導入CoreLocation,如下:

import CoreLocation

接著,讓AppDelegate遵循CLLocationManagerDelegate協議,在AppDelegate .swift底部添加如下代碼(在類結束符下面)

// MARK: - CLLocationManagerDelegate
extension AppDelegate: CLLocationManagerDelegate {
}

在這之前,你需要初始化location manager,設置它的delegate。

給AppDelegate添加一個locationManager屬性,用CLLocationManager對象實例化它:

let locationManager=CL LocationManager()

然後在application(_:didFinishLaunchingWithOptions:):添加如下代碼:

locationManager.delegate=self

要知道app中所有的location manager都能通過startMonitoringForRegion(_:)共同監控你添加的區域(location manager是單例)。因此最後一步,只需要在走出某個區域時,對Core Location何時喚醒app作出反應就行了。

在AppDelegate.swift底部的類extension裡添加如下代碼:

func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
  if let beaconRegion = region as? CLBeaconRegion {
    var notification = UILocalNotification()
    notification.alertBody = "Are you forgetting something?"
    notification.soundName = "Default"
    UIApplication.sharedApplication().presentLocalNotificationNow(notification)
  }
}

當你走出某個區域,location manager就會調用上述方法,這是app一大亮點。假如離手提包越來越近,app就不需要提醒你,只有離它太遠才會觸發。

首先需要確定區域是否是CLBeaconRegion,因為當執行地理區域監控時,它還有可能是CLCircularRegion。然後用"Are you forgetting something?"消息發一個本地推送。

在iOS 8之後,app使用本地推送或者遠程推送,必須注冊推送的類型。系統給用戶權限來限制不同類型推送的界面顯示。假如app不能使用這些推送類型,即使它們是在推送載荷被指定過,系統也不會對app icon標記,不會顯示提示信息,或者沒有提示音效。

在application(:_didFinishLaunchingWithOptions:):的最上面添加如下代碼:

let notificationType:UIUserNotificationType = UIUserNotificationType.Sound | UIUserNotificationType.Alert
let notificationSettings = UIUserNotificationSettings(forTypes: notificationType, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)

這段代碼就是當app收到一個推送時,就會顯示一段提示信息,並且播放一段音效。

構建項目;確保app能監測到至少一個iBeacon設備,點擊Home按鈕讓app進入後台模式--這是現實生活中的場景,當你在處理其他事情的時候,比如處理Ray Wenderlich的另一個app時,希望這個後台app能通知你。現在遠離iBeacon,一旦離得足夠遠,有就會收到這個推送,如下:

notification-281x500.jpg

鎖屏上的通知

備注:蘋果系統以未公開的方式延遲退出推送。這樣設計可能方便當你在區域范圍的邊緣游蕩或者iBeacon信號被干擾時,app不會接收之前的推送。以筆者的經驗來說,在iBeacon離開區域范圍一分鐘時推送就會退出。

更進一步?


還沒有為你的代碼綁定iBeacon嗎?從這兒下載最終的項目(here),教程所說的全在這裡。

你已經有了一款很有用的app,來監控哪些比較難追蹤的東西。加一些額外的思考和編程功底,你還可以給app添加更多有用的功能:

  • 通知用戶那個iBeacon移出了監控范圍

  • 重復推送,確保用戶能看到它

  • 提醒用戶iBeacon何時又返回監控范圍

這篇iBeacons教程僅僅只是揭開iBeacon所有功能的冰山一角。

iBeacon不局限於傳統app;你還可以在Passbook中使用它。比如,當你去看電影時,可以提供Passbook通行證當作電影票。當顧客走到附近有iBeacon設備的檢票員面前時,app自動在iPhone上顯示電影票!

對本篇教程有任何疑問或者意見,或者你有與iBeacon相關的好點子,歡迎加入我們的討論!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved