1. 程式人生 > >ios開發之藍芽4.0技術

ios開發之藍芽4.0技術

前言

前端時間,同學在做專案過程中遇到關於藍芽方面的問題,今天我就給大家進行詳細的進行講解下藍芽在iOS開發中的具體實現.在介紹藍芽前,大家要搞清楚什麼是藍芽?

什麼是藍芽?

隨著藍芽低功耗技術BLE(Bluetooth Low Energy)的發展,藍芽技術正在一步步成熟,如今的大部分移動裝置都配備有藍芽4.0,相比之前的藍芽技術耗電量大大降低。從iOS的發展史也不難看出蘋果目前對藍芽技術也是越來越關注,例如蘋果於2013年9月釋出的iOS7就配備了iBeacon技術,這項技術完全基於藍芽傳輸。但是眾所周知蘋果的裝置對於許可權要求也是比較高的,因此在iOS中並不能像Android

一樣隨意使用藍芽進行檔案傳輸(除非你已經越獄)。知道什麼是藍芽之後,那麼在iOS中進行藍芽傳輸應用開發常用的框架有哪幾種呢?

藍芽在開發中的框架有哪些?

  1. GameKit.framework:iOS7之前的藍芽通訊框架,從iOS7開始過期,但是目前多數應用還是基於此框架。
  2. MultipeerConnectivity.framework:iOS7開始引入的新的藍芽通訊開發框架,用於取代GameKit。
  3. CoreBluetooth.framework:功能強大的藍芽開發框架,要求裝置必須支援藍芽4.0。

藍芽在開發中的框架優缺點?

現在就給大家來總結下這三種框架的優缺點. 
前兩個框架使用起來比較簡單,但是缺點也比較明顯:僅僅支援iOS裝置,傳輸內容僅限於沙盒或者照片庫中使用者選擇的檔案,並且第一個框架只能在同一個應用之間進行傳輸(一個iOS裝置安裝應用A,另一個iOS裝置上安裝應用B是無法傳輸的)。當然CoreBluetooth就擺脫了這些束縛,它不再侷限於iOS裝置之間進行傳輸,你可以通過iOS裝置向

Android、Windows Phone以及其他安裝有藍芽4.0晶片的智慧裝置傳輸,因此也是目前智慧家居、無線支付等熱門智慧裝置所推崇的技術。

藍芽框架之GameKit框架

其實從名稱來看這個框架並不是專門為了支援藍芽傳輸而設計的,它是為遊戲設計的。而很多遊戲中會用到基於藍芽的點對點資訊傳輸,因此這個框架中集成了藍芽傳輸模組。前面也說了這個框架本身有很多限制,但是在iOS7之前的很多藍芽傳輸都是基於此框架的,所以有必要對它進行了解。GameKit中的藍芽使用設計很簡單,並沒有給開發者留有太多的複雜介面,而多數連線細節開發者是不需要關注的。GameKit中提供了兩個關鍵類來操作藍芽連線:

GKPeerPickerController:藍芽查詢、連線用的檢視控制器,通常情況下應用程式A開啟後會呼叫此控制器的show方法來展示一個藍芽查詢的檢視,一旦發現了另一個同樣在查詢藍芽連線的客戶客戶端B就會出現在檢視列表中,此時如果使用者點選連線B,B客戶端就會詢問使用者是否允許A連線B,如果允許後A和B之間建立一個藍芽連線。

GKSession:連線會話,主要用於傳送和接受傳輸資料。一旦A和B建立連線GKPeerPickerController的代理方法會將A、B兩者建立的會話(GKSession)物件傳遞給開發人員,開發人員拿到此物件可以傳送和接收資料。

其實理解了上面兩個類之後,使用起來就比較簡單了,下面就以一個圖片傳送程式來演示GameKit中藍芽的使用。此程式一個客戶端執行在模擬器上作為客戶端A,另一個執行在iPhone真機上作為客戶端B(注意A、B必須運行同一個程式,GameKit藍芽開發是不支援兩個不同的應用傳輸資料的)。兩個程式執行之後均呼叫GKPeerPickerController來發現周圍藍芽裝置,一旦A發現了B之後就開始連線B,然後iOS會詢問使用者是否接受連線,一旦接受之後就會呼叫GKPeerPickerController的-(void)peerPickerController:(GKPeerPickerController )picker didConnectPeer:(NSString )peerID toSession:(GKSession *)session代理方法,在此方法中可以獲得連線的裝置id(peerID)和連線會話(session);此時可以設定會話的資料接收控制代碼(相當於一個代理)並儲存會話以便傳送資料時使用;一旦一端(假設是A)呼叫會話的sendDataToAllPeers: withDataMode: error:方法傳送資料,此時另一端(假設是B)就會呼叫控制代碼的 
- (void) receiveData:(NSData )data fromPeer:(NSString )peer inSession: (GKSession )session context:(void )context方法,在此方法可以獲得傳送資料並處理。下面是程式程式碼:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;">"ViewController.h"</span></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;"><GameKit/GameKit.h></span></span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewController</span> ()<<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">GKPeerPickerControllerDelegate</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">UIImagePickerControllerDelegate</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">UINavigationBarDelegate</span>></span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">weak</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">IBOutlet</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImageView</span> *imageView;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//照片顯示檢視</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>) GKSession *session;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//藍芽連線會話</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@implementation</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewController</span></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - 控制器檢視方法</span> - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)viewDidLoad { [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span> viewDidLoad]; GKPeerPickerController *pearPickerController=[[GKPeerPickerController alloc]init]; pearPickerController<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.delegate</span>=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>; [pearPickerController show]; } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - UI事件</span> - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">IBAction</span>)selectClick:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIBarButtonItem</span> *)sender { UIImagePickerController *imagePickerController=[[UIImagePickerController alloc]init]; imagePickerController<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.delegate</span>=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> presentViewController:imagePickerController animated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; } - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">IBAction</span>)sendClick:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIBarButtonItem</span> *)sender { NSData *data=UIImagePNGRepresentation(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.imageView</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.image</span>); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSError</span> *error=<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.session</span> sendDataToAllPeers:data withDataMode:GKSendDataReliable error:&error]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (error) { <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"傳送圖片過程中發生錯誤,錯誤資訊:%@"</span>,error<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.localizedDescription</span>); } } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - GKPeerPickerController代理方法</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 連線到某個裝置 * * @param picker 藍芽點對點連線控制器 * @param peerID 連線裝置藍芽傳輸ID * @param session 連線會話 */</span> -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *)peerID toSession:(GKSession *)session{ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.session</span>=session; <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"已連線客戶端裝置:%@."</span>,peerID); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//設定資料接收處理控制代碼,相當於代理,一旦資料接收完成呼叫它的-receiveData:fromPeer:inSession:context:方法處理資料</span> [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.session</span> setDataReceiveHandler:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> withContext:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; [picker dismiss];<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//一旦連線成功關閉視窗</span> } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - 藍芽資料接收方法</span> - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>) receiveData:(NSData *)data fromPeer:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *)peer inSession: (GKSession *)session context:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> *)context{ <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImage</span> *image=[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImage</span> imageWithData:data]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.imageView</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.image</span>=image; UIImageWriteToSavedPhotosAlbum(image, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"資料傳送成功!"</span>); } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - UIImagePickerController代理方法</span> -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSDictionary</span> *)info{ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.imageView</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.image</span>=[info objectForKey:UIImagePickerControllerOriginalImage]; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> dismissViewControllerAnimated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; } -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)imagePickerControllerDidCancel:(UIImagePickerController *)picker{ [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> dismissViewControllerAnimated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li></ul>

執行效果(左側是真機,右側是模擬器,程式演示了兩個客戶端互發圖片的場景:首先是模擬器傳送圖片給真機,然後真機發送圖片給模擬器) 
這裡寫圖片描述

藍芽框架之MultipeerConnectivity框架

前面已經說了GameKit相關的藍芽操作類從iOS7已經全部過期,蘋果官方推薦使用MultipeerConnectivity代替。但是應該瞭解,MultipeerConnectivity.framework並不僅僅支援藍芽連線,準確的說它是一種支援Wi-Fi網路、P2P Wi-Fi已經藍芽個人區域網的通訊框架,它遮蔽了具體的連線技術,讓開發人員有統一的介面程式設計方法。通過MultipeerConnectivity連線的節點之間可以安全的傳遞資訊、流或者其他檔案資源而不必通過網路服務。此外使用MultipeerConnectivity進行近場通訊也不再侷限於同一個應用之間傳輸,而是可以在不同的應用之間進行資料傳輸(當然如果有必要的話你仍然可以選擇在一個應用程式之間傳輸)。

要了解MultipeerConnectivity的使用必須要清楚一個概念:廣播(Advertisting)和發現(Disconvering),這很類似於一種Client-Server模式。假設有兩臺裝置A、B,B作為廣播去傳送自身服務,A作為發現的客戶端。一旦A發現了B就試圖建立連線,經過B同意二者建立連線就可以相互發送資料。在使用GameKit框架時,A和B既作為廣播又作為發現,當然這種情況在MultipeerConnectivity中也很常見。

A.廣播

無論是作為伺服器端去廣播還是作為客戶端去發現廣播服務,那麼兩個(或更多)不同的裝置之間必須要有區分,通常情況下使用MCPeerID物件來區分一臺裝置,在這個裝置中可以指定顯示給對方檢視的名稱(display name)。另外不管是哪一方,還必須建立一個會話MCSession用於傳送和接受資料。通常情況下會在會話的-(void)session:(MCSession )session peer:(MCPeerID )peerID didChangeState:(MCSessionState)state代理方法中跟蹤會話狀態(已連線、正在連線、未連線);在會話的-(void)session:(MCSession )session didReceiveData:(NSData )data fromPeer:(MCPeerID *)peerID代理方法中接收資料;同時還會呼叫會話的-(void)sendData: toPeers:withMode: error:方法去傳送資料。

廣播作為一個伺服器去釋出自身服務,供周邊裝置發現連線。在MultipeerConnectivity中使用MCAdvertiserAssistant來表示一個廣播,通常建立廣播時指定一個會話MCSession物件將廣播服務和會話關聯起來。一旦呼叫廣播的start方法周邊的裝置就可以發現該廣播並可以連線到此服務。在MCSession的代理方法中可以隨時更新連線狀態,一旦建立了連線之後就可以通過MCSession的connectedPeers獲得已經連線的裝置。

B.發現

前面已經說過作為發現的客戶端同樣需要一個MCPeerID來標誌一個客戶端,同時會擁有一個MCSession來監聽連線狀態併發送、接受資料。除此之外,要發現廣播服務,客戶端就必須要隨時查詢服務來連線,在MultipeerConnectivity中提供了一個控制器MCBrowserViewController來展示可連線和已連線的裝置(這類似於GameKit中的GKPeerPickerController),當然如果想要自己定製一個介面來展示裝置連線的情況你可以選擇自己開發一套UI介面。一旦通過MCBroserViewController選擇一個節點去連線,那麼作為廣播的節點就會收到通知,詢問使用者是否允許連線。由於初始化MCBrowserViewController的過程已經指定了會話MCSession,所以連線過程中會隨時更新會話狀態,一旦建立了連線,就可以通過會話的connected屬性獲得已連線裝置並且可以使用會話傳送、接受資料。

下面用兩個不同的應用程式來演示使用MultipeerConnectivity的使用過程,其中一個應用執行在模擬器中作為廣播節點,另一個執行在iPhone真機上作為發現節點,並且實現兩個節點的圖片互傳。

首先看一下作為廣播節點的程式:

介面: 
這裡寫圖片描述 
點選“開始廣播”來發布服務,一旦有節點連線此服務就可以使用“選擇照片”來從照片庫中選取一張圖片併發送到所有已連線節點。 
程式:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;">"ViewController.h"</span></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;"><MultipeerConnectivity/MultipeerConnectivity.h></span></span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewController</span> ()<<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MCSessionDelegate</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MCAdvertiserAssistantDelegate</span>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">UIImagePickerControllerDelegate</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">UINavigationControllerDelegate</span>></span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>) MCSession *session; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>) MCAdvertiserAssistant *advertiserAssistant; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>) UIImagePickerController *imagePickerController; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">weak</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">IBOutlet</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImageView</span> *photo; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@implementation</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewController</span></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - 控制器檢視事件</span> - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)viewDidLoad { [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span> viewDidLoad]; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//建立節點,displayName是用於提供給周邊裝置檢視和區分此服務的</span> MCPeerID *peerID=[[MCPeerID alloc]initWithDisplayName:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"KenshinCui_Advertiser"</span>]; _session=[[MCSession alloc]initWithPeer:peerID]; _session<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.delegate</span>=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//建立廣播</span> _advertiserAssistant=[[MCAdvertiserAssistant alloc]initWithServiceType:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"cmj-stream"</span> discoveryInfo:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span> session:_session]; _advertiserAssistant<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.delegate</span>=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>; } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - UI事件</span> - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">IBAction</span>)advertiserClick:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIBarButtonItem</span> *)sender { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//開始廣播</span> [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.advertiserAssistant</span> start]; } - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">IBAction</span>)selectClick:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIBarButtonItem</span> *)sender { _imagePickerController=[[UIImagePickerController alloc]init]; _imagePickerController<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.delegate</span>=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> presentViewController:_imagePickerController animated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - MCSession代理方法</span> -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{ <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"didChangeState"</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (state) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MCSessionStateConnected: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"連線成功."</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MCSessionStateConnecting: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"正在連線..."</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">default</span>: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"連線失敗."</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//接收資料</span> -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{ <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"開始接收資料..."</span>); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImage</span> *image=[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImage</span> imageWithData:data]; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.photo</span> setImage:image]; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//儲存到相簿</span> UIImageWriteToSavedPhotosAlbum(image, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>); } <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - MCAdvertiserAssistant代理方法</span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#pragma mark - UIImagePickerController代理方法</span> -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSDictionary</span> *)info{ <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIImage</span> *image=[info objectForKey:UIImagePickerControllerOriginalImage]; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.photo</span> setImage:image]; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//傳送資料給所有已連線裝置</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSError</span> *error=<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>; [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.session</span> sendData:UIImagePNGRepresentation(image) toPeers:[<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.session</span> connectedPeers] withMode:MCSessionSendDataUnreliable error:&error]; <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"開始傳送資料..."</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (error) { <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"傳送資料過程中發生錯誤,錯誤資訊:%@"</span>,error<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.localizedDescription</span>); } [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.imagePickerController</span> dismissViewControllerAnimated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; } -(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)imagePickerControllerDidCancel:(UIImagePickerController *)picker{ [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.imagePickerController</span> dismissViewControllerAnimated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>]; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: b