iOS開發實用技術之二維碼

一. 二維碼生成和掃描

1. 二維碼概念

二維碼, 是用某種特定的幾何圖形按必定規律在平面(二維方向上)分佈的黑白相間的圖形記錄數據符號信息的;

2. 二維碼的使用場景

● 信息獲取(名片、地圖、WIFI密碼、資料)
    ● 網站跳轉(跳轉到微博、手機網站、網站)
    ● 廣告推送(用戶掃碼,直接瀏覽商家推送的視頻、音頻廣告)
    ● 手機電商(用戶掃碼、手機直接購物下單)
    ● 防僞溯源(用戶掃碼、便可查看生產地;同時後臺能夠獲取最終消費地)
    ● 優惠促銷(用戶掃碼,下載電子優惠券,抽獎)
    ● 會員管理(用戶手機上獲取電子會員信息、VIP服務)
    ● 手機支付(掃描商品二維碼,經過銀行或第三方支付提供的手機端通道完成支付)

3. 二維碼生成方式

> 從iOS7開始集成了二維碼的生成和讀取功能
    > 此前被普遍使用的zbarsdk目前不支持64位處理器

4. 生成二維碼步驟

  1. 導入CoreImage框架session

    #import <CoreImage/CoreImage.h>
  2. 經過濾鏡CIFilter生成二維碼框架

    > 1. 實例化二維碼濾鏡
         CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    
     > 2. 恢復濾鏡的默認屬性
         [filter setDefaults];
    
     > 3. 將字符串轉換成NSData
         NSData *data = [@"小碼哥" dataUsingEncoding:NSUTF8StringEncoding];
    
     > 4. 經過KVC設置濾鏡inputMessage數據
         [filter setValue:data forKey:@"inputMessage"];
    
     > 5. 得到濾鏡輸出的圖像
         CIImage *outputImage = [filter outputImage];
    
     > 6. 將CIImage轉換成UIImage,並放大顯示
         return [UIImage imageWithCIImage:outputImage scale:20.0 orientation:UIImageOrientationUp];

5. 讀取二維碼

讀取二維碼須要導入AVFoundation框架ide

利用攝像頭識別二維碼中的內容(模擬器不行)工具

  1. 實例化拍攝設備測試

    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  2. 設置輸入設備網站

    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
  3. 設置元數據輸出ui

    3.1 實例化拍攝元數據輸出
         AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    
     3.2 設置輸出數據代理
         [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
     3.3 設置掃描取景範圍(rectOfInterest 都是按照橫屏來計算的 因此當豎屏的狀況下 x軸和y軸要交換一下)
     CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
     CGFloat screenH = [UIScreen mainScreen].bounds.size.height;
     CGFloat scanW = screenW * 0.6;
     CGRect scanRect = CGRectMake((screenW - scanW) * 0.5, (screenH - scanW) * 0.5, scanW, scanW);
     output.rectOfInterest = CGRectMake(scanRect.origin.y / screenH, scanRect.origin.x / screenW, scanRect.size.height / screenH, scanRect.size.width / screenW);
    
     3.4 設置掃描區域的邊框
     UIView *scanV = [[UIView alloc] initWithFrame:scanRect];
     [self.view addSubview:scanV];
     scanV.layer.borderWidth = 2;
     scanV.layer.borderColor = [UIColor redColor].CGColor;
  4. 添加拍攝會話spa

    4.1 實例化拍攝會話
         AVCaptureSession *session = [[AVCaptureSession alloc] init];
    
     4.2 添加會話輸入
         [session addInput:input];
    
     4.3 添加會話輸出
         [session addOutput:output];
    
     4.3 設置輸出數據類型,須要將元數據輸出添加到會話後,才能指定元數據類型,不然會報錯
         [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
  5. 視頻預覽圖層代理

    5.1 實例化預覽圖層
         AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession:_session];
         preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
         preview.frame = self.view.bounds;
    
     5.2 將圖層插入當前視圖
         [self.view.layer addSublayer:preview];
         self.previewLayer = preview;
  6. 啓動會話指針

    [_session startRunning];

二. 內存分析

主要目的就是爲了檢測程序是否存在內存泄露

1. 靜態內存分析(Analyze)

做用: 
    > 邏輯錯誤:訪問未初始化的變量, 野指針等;
    > 聲明錯誤:從未使用過的對象;
    > 內存管理錯誤:如內存泄漏等;
分析方法:
    > 靜態內存分析是不運行程序,直接對代碼進行分析.
    > 根據代碼的上下文的語法結構,來分析是否有內存泄露
缺點:
    > 不必定準確,可是若是發現有提示,那麼去結合上下文看一下,這裏的代碼是否有問題

場景演練:
    > MRC 下橋接 - Foundation 和 CoreFoundation框架的數據類型轉換
    > ARC 下橋接 - Foundation 和 CoreFoundation框架的數據類型轉換

2. 內存分配

做用:
    > 查看是內存的分配狀況
    > 查看內存是否有釋放
場景演示:
    > UIImage 的兩種建立方法測試
    > imageNamed:
    > imageWithContentOfFile:

3. 動態內存分析

做用:
    > 檢測程序在運行過程當中是否存在內存泄露
場景演示:
    > 模擬循環引用, 測試內存泄露

三. 通信錄獲取

1. 通信錄應用場景

1. 最多見的是一些即時通信APP, 關聯聯繫人;

2. 通信錄獲取方案

  1. AddressBookUI.framework 框架

    > 提供了聯繫人列表界面、聯繫人詳情界面、添加聯繫人界面等
         > 通常用於選擇聯繫人
  2. AddressBook.framework 框架

    > 純C語言的API,僅僅是得到聯繫人數據
         > 沒有提供UI界面展現,須要本身搭建聯繫人展現界面
         > 裏面的數據類型大部分基於Core Foundation框架,使用起來極其蛋疼
  3. ContactsUI.framework (iOS9.0最新框架)

    > 方案1/方案2 的替代品
  4. 第三方框架 RHAddressBook

    > 對AddressBook.framework框架的封裝

3. 獲取通信錄-AddressBookUI

1. 實現步驟

  1. 建立選擇聯繫人的控制器
  2. 設置代理(用來接收用戶選擇的聯繫人信息)
  3. 彈出聯繫人控制器
  4. 實現代理
  5. 在對應的代理方法中獲取聯繫人信息

2. 具體代碼實現

  1. 建立選擇聯繫人的控制器

    ABPeoplePickerNavigationController *ppnc = [[ABPeoplePickerNavigationController alloc] init];
  2. 設置代理(用來接收用戶選擇的聯繫人信息)

    ppnc.peoplePickerDelegate = self;
  3. 彈出聯繫人控制器

    [self presentViewController:ppnc animated:YES completion:nil];
  4. 實現代理

    // 1. 選中某個聯繫人時調用
     - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
     {
    
     }
    
     // 2. 選中某個聯繫人某個屬性時調用
     - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
     {
    
     }
    
     // 3. 點擊了取消按鈕會執行該方法
     - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
     {
    
     }
  5. 在對應的代理方法中獲取聯繫人信息

    // 1. 獲取選中聯繫人的姓名(姓lastname和名firstname)
     CFStringRef firstname = ABRecordCopyValue(person, kABPersonFirstNameProperty);
     CFStringRef lastname = ABRecordCopyValue(person, kABPersonLastNameProperty);
     NSString *firstName = (__bridge_transfer NSString *)(firstname);
     NSString *lastName = (__bridge_transfer NSString *)(lastname);
     NSLog(@"%@ %@", firstName, lastName);
    
     // 2. 獲取聯繫人的電話號碼
     ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
     CFIndex count = ABMultiValueGetCount(phones);
     for (CFIndex i = 0; i < count; i++) {
         NSString *phoneLabel = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(phones, i);
         NSString *phoneValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, i);
         NSLog(@"label : %@ value : %@", phoneLabel, phoneValue);
     }
    
     // 3. 釋放再也不使用的對象
     CFRelease(phones);

4. 獲取通信錄-AddressBook

1. 實現步驟

  1. 請求受權
  2. 判斷受權狀態, 若是已受權, 則繼續; 未受權, 則提示用戶, 並返回;
  3. 建立通信錄對象
  4. 從通訊錄對象中, 獲取全部的聯繫人
  5. 遍歷全部的聯繫人
  6. 釋放再也不使用的對象

2. 代碼實現

  1. 請求受權

    // 1.獲取受權的狀態
     ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    
     // 2.判斷受權狀態,若是是未決定狀態,才須要請求
     if (status == kABAuthorizationStatusNotDetermined) {
         // 2.1.建立通訊錄對象
         ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    
         // 2.2.請求受權
         ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
         if (granted) {
             NSLog(@"受權成功");
         } else {
             NSLog(@"受權失敗");
         }
     });
     }
  2. 判斷受權狀態, 若是已受權, 則繼續; 未受權, 則提示用戶, 並返回;

    // 1.獲取受權的狀態
     ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    
     // 2.若是用戶已經受權
     if (status != kABAuthorizationStatusAuthorized) return;
  3. 建立通信錄對象

    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
  4. 從通訊錄對象中, 獲取全部的聯繫人

    CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
  5. 遍歷全部的聯繫人

    // 5.遍歷全部的聯繫人(每個聯繫人都是一條記錄)
     CFIndex peopleCount = CFArrayGetCount(peopleArray);
     for (CFIndex i = 0; i < peopleCount; i++) {
    
     // 6.獲取到聯繫人
     ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i);
    
     // 7.獲取姓名
     NSString *lastname = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
     NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
     NSLog(@"%@ %@", lastname, firstName);
     }
  6. 釋放再也不使用的對象

    CFRelease(peopleArray);
     CFRelease(addressBook);

5. 獲取通信錄-第三方框架RHAddressBook

1. 實現步驟

  1. 集成框架
  2. 使用框架獲取全部聯繫人信息

2. 具體實現

  1. 集成框架

    > 1. 將整個工程拖入項目
     > 2. 添加工程依賴
         Build Phases -> Target Dependencies -> + 
     > 3. 添加連接項
         Build Settings -> Other Linker Flags -> -ObjC -all_load
     > 4. 導入框架頭文件
         #import <RHAddressBook/AddressBoook.h>
  2. 使用框架獲取全部聯繫人信息

    > 請求受權    
         // 1. 獲取受權狀態
         RHAuthorizationStatus status = [RHAddressBook authorizationStatus];
    
         if (status == RHAuthorizationStatusNotDetermined)
         {
             // 2. 建立通信錄對象
             RHAddressBook *addressBook = [[RHAddressBook alloc] init];
    
             // 3. 請求受權
             [addressBook requestAuthorizationWithCompletion:^(bool granted, NSError *error) {
             if (granted)
             {
                 NSLog(@"受權成功!");
             }
             else
             {
                 NSLog(@"受權失敗");
             }
             }];
         }
    
     > 獲取聯繫人信息
    
         // 1. 判斷當前受權狀態
         RHAuthorizationStatus status = [RHAddressBook authorizationStatus];
         if (status != RHAuthorizationStatusAuthorized) {
             return;
         }
    
         // 2. 建立通信錄對象
         RHAddressBook *addressBook = [[RHAddressBook alloc] init];
    
         // 3. 獲取全部聯繫人
         NSArray *peoples = addressBook.people;
    
         // 4. 遍歷全部聯繫人
         for (RHPerson *person in peoples)
         {
             // 4.1 獲取聯繫人姓名
             NSString *firstName = person.firstName;
             NSString *lastName = person.lastName;
             NSLog(@"%@---%@", firstName, lastName);
    
             // 4.2 獲取聯繫人電話
             RHMultiStringValue *mv = person.phoneNumbers;
             for (int i = 0; i < mv.count; i ++)
             {
                 // 4.2.1 獲取電話標籤
                 NSString *label = [mv labelAtIndex:i];
    
                 // 4.2.2 獲取電話號碼
                 NSString *phone = [mv valueAtIndex:i];
                 NSLog(@"%@--%@", label, phone);
    
             }
         }

四. 換膚

1. 換膚的應用場景?

通常應用在某些APP , 在節假日更換主題, 或者切換白天或者夜間模式時使用.

2. 換膚的實現方案

1. 方案1

1. 將圖片以及顏色按照主題命名,使用虛擬文件夾, 直接在各個控制器針對不一樣主題單獨設置
    缺點 : 
        1. 擴展性差, 增長一個主題很是複雜
        2. 代碼分散, 可維護性差
        3. 使用虛擬文件夾保存資源, 容易與其餘資源容易產生衝突

2. 方案2

2. 抽取公共的切換主題類, 使用物理文件夾, 並將圖片按照應用場景命名, 不按照主題命名, 可是按照主題劃分文件夾
優勢:
    1. 擴展性好, 增長主題簡單
    2. 功能代碼集中, 方便維護
    3. 資源包單獨管理, 不會產生資源衝突

演練步驟

1. 實現基本的換膚功能
2. 使用用戶偏好記錄當前皮膚主題
3. 抽取公共的皮膚管理類, 簡化控制器邏輯
4. 驗證多處加載主題情景
5. 加載主題文字顏色功能實現

五. 硬件信息的獲取

1. 應用場景

例如
    QQ空間APP 發說說時, 出現的什麼什麼型號的手機;
    迅雷APP 下載文件時提示剩餘空間,已用空間

2. 實現方案

直接經過第三方工具類(UIDevice的分類), 進行獲取對應信息
緣由: 本身寫起來比較複雜, 不少C語言的東西, 並且沒有必要;

3. 框架完善

框架存在問題: 該第三方框架從2012年就中止更新了,意味着12年以後的手機型號都沒有, 須要手動添加, 修改框架
解決方案: 找到對應的實現方法, 使用真機進行測試, 手動新增手機型號
相關文章
相關標籤/搜索