二維碼(Quick Response Code,簡稱QR Code)是由水平和垂直兩個方向上的線條設計而成的一種二維條形碼(barcode)。可以編碼網址、電話號碼、文本等內容,能夠存儲大量的數據信息。自iOS 7以來,二維碼的生成和讀取只需要使用Core Image框架和AVFoundation框架就能輕松實現。在這里,我們主要介紹二維碼的生成。關于二維碼的讀取,在 使用AVFoundation讀取二維碼 文章中有詳細介紹。
1 二維碼的生成
生成一個二維碼也就是根據提供的數據內容轉換成一張二維碼圖像。從iOS 7開始,我們只需要使用CIFilter中的CIQRCodeGenerator就可以輕易實現。只不過這樣生成的二維碼圖像是一個CIImage對象,如果要在圖像視圖中顯示,需要將其轉換為UIImage對象。具體步驟如下:
①、使用名為 CIQRCodeGenerator 的過濾器創建一個CIFilter對象。
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]
②、為CIFilter對象設置 inputMessage 和 inputCorrectionLevel 參數。
③、使用CIFilter對象的 outputImage
屬性獲取生成的二維碼圖像
CIImage *outputImage = filter.outputImage;
④、對生成的二維碼圖像進行縮放。
由于生成的二維碼圖像尺寸一般都比較小,為了避免模糊,通常需要對它進行縮放以適應圖像視圖的大小。其縮放比例一般為圖像視圖寬度(或高度)與二維碼圖像寬度(或高度)的比值。
CGFloat scaleX = imageView.bounds.size.width / qrcodeImage.extent.size.width;CGFloat scaleY = imageView.bounds.size.height / qrcodeImage.extent.size.height;CIImage *transformedImage = [qrcodeImage imageByApplyingTransform:CGAffineTransformMakeScale(scaleX, scaleY)];
⑤、將二維碼圖像轉換為UIImage對象。
imageView.image = [UIImage imageWithCIImage:transformedImage];
2 應用示例
下面,我們就做一個如下圖所示的二維碼生成器:
其中主要實現的功能有:
2.1 創建項目
打開 Xcode ,創建一個新的項目( File/New/Project.. .),選擇 iOS 一欄下的 Application 中的 Single View Application 模版,然后點擊 Next ,填寫項目選項。在 Product Name 中填寫 QRCodeGeneratorDemo ,選擇 Objective-C 語言,點擊 Nex t,選擇文件位置,并單擊 Create 創建項目。
2.2 構建界面
打開 Main.storyboard 文件,在當前控制器中嵌入導航控制器,并添加標題 QR Code Generator :
在視圖控制器中添加文本框、按鈕、圖像視圖等,布局如下:
其中各元素及作用:
打開輔助編輯器,將storyboard中的元素連接到代碼中:
2.3 添加代碼
2.3.1 生成二維碼圖像
由于使用CIFilter對象生成的二維碼圖像將是一個CIImage對象,所以需要先在 ViewController.m 文件的接口部分聲明一個CIImage對象的屬性:
@property (strong, nonatomic) CIImage *qrcodeImage;
然后在實現部分添加 generateQRCodeImage
方法及代碼:
- (void)generateQRCodeImage{ NSData *data = [self.textField.text dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:NO]; // 創建并設置CIFilter對象 CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; [filter setValue:data forKey:@"inputMessage"]; [filter setValue:@"Q" forKey:@"inputCorrectionLevel"]; // 獲取生成的CIImage對象 self.qrcodeImage = filter.outputImage; // 轉換成UIImage對象,并顯示在圖像視圖中 self.imageView.image = [UIImage imageWithCIImage:self.qrcodeImage];}
在 clickButton:
方法中調用該方法:
- (IBAction)clickButton:(id)sender{ [self generateQRCodeImage];}
此時,運行程序,在文本框中輸入內容,點擊按鈕就可以看到生成的二維碼:
仔細的話你會發現這里的二維碼比較模糊,這是由于生成的二維碼尺寸較小,在imageView中顯示時被拉伸導致的。下面我們就通過調整二維碼的縮放來解決圖像模糊的問題。
修改 generateQRCodeImage
方法中的代碼如下:
- (void)generateQRCodeImage{ ... // 獲取生成的CIImage對象 self.qrcodeImage = filter.outputImage; // 縮放CIImage對象 CGFloat scaleX = self.imageView.bounds.size.width / self.qrcodeImage.extent.size.width; CGFloat scaleY = self.imageView.bounds.size.height / self.qrcodeImage.extent.size.height; CIImage *transformedImage = [self.qrcodeImage imageByApplyingTransform:CGAffineTransformMakeScale(scaleX, scaleY)]; // 將調整后的CIImage對象轉換成UIImage對象,并顯示在圖像視圖中 self.imageView.image = [UIImage imageWithCIImage:transformedImage];}
再次運行,你會看到一張清晰的二維碼:
2.3.2 刪除二維碼圖像
在這個demo中,按鈕具有雙重作用:即生成二維碼和刪除二維碼。因此需要在 clickButton:
方法中先判斷二維碼圖像是否存在:
修改 clickButton:
方法中的代碼如下:
- (IBAction)clickButton:(id)sender{ if (self.qrcodeImage == nil) { [self generateQRCodeImage]; [self.button setTitle:@"Clear" forState:UIControlStateNormal]; } else { [self clearQRCodeImage]; [self.button setTitle:@"Generate" forState:UIControlStateNormal]; }}
下面我們就添加 clearQRCodeImage
方法并實現它:
- (void)clearQRCodeImage{ self.imageView.image = nil; self.qrcodeImage = nil; self.textField.text = nil;}
至此,二維碼的生成和刪除已基本完成,但為了有良好的體驗,我們還需要考慮下面的情況:
于是,在 clickButton:
方法中添加下面的代碼:
- (IBAction)clickButton:(id)sender{ if (self.qrcodeImage == nil) { if ([self.textField.text isEqualToString:@""]) { return; } [self generateQRCodeImage]; [self.textField resignFirstResponder]; [self.button setTitle:@"Clear" forState:UIControlStateNormal]; } else { [self clearQRCodeImage]; [self.button setTitle:@"Generate" forState:UIControlStateNormal]; } self.textField.enabled = !self.textField.enabled; self.slider.hidden = !self.slider.hidden;}
最后,考慮到程序啟動時滑動條不應該顯示(滑動條只在生成二維碼時出現),還需要在 viewDidLoad
方法中設置其 hidden
屬性:
- (void)viewDidLoad{ [super viewDidLoad]; self.slider.hidden = YES;}
可以再次運行試下。
2.3.3 縮放二維碼圖像
縮放顯示的二維碼主要是通過拖動滑動條縮放image View來完成的。在實現部分找到 changeScale:
方法,并添加代碼下面的代碼即可:
- (IBAction)changeScale:(id)sender{ self.imageView.transform = CGAffineTransformMakeScale((CGFloat)self.slider.value, (CGFloat)self.slider.value);}
需要注意的是, self.slider.value
是 float
類型,而 CGAffineTransformMakeScale
方法的參數是 CGFloat
類型,因此在上面的代碼中進行了類型轉換。
2.3.4 保存二維碼圖像
將生成的二維碼圖片保存到相機膠卷相冊主要是使用 UIImageWriteToSavedPhotosAlbum 函數來實現的。其完整聲明如下:
其中各參數及含義:
image
:表示要保存到相冊的圖像。
completionTarget
:可選的參數,表示圖片保存后,調用完成選擇器(completionSelector)的對象。
completionSelector
:可選的方法,表示圖片保存后,completionTarget對象要調用的方法(即回調方法)。該方法應符合以下簽名:
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo;
contextInfo
:可選的參數,用于提供一個上下文信息以通過參數傳遞給completionSelector。
在這里我們的大致思路是:首先在imageView中添加單擊手勢,當用戶點擊二維碼圖像時會彈出一個提示框詢問是否保存,如果用戶點擊保存按鈕,那么就將二維碼保存到相冊中。下面是具體實現:
打開 Main.storyboard 文件,從對象庫中找到點擊手勢(Tap Gesture Recognizer),將其添加到視圖控制器的Image View上,完成之后會在控制器的頂部看到它:
打開輔助編輯器,將其連接到代碼中:
為了使image View能響應手勢操作,需要在 viewDidLoad
中設置image View的 userInteractionEnabled
屬性:
- (void)viewDidLoad{ ... self.imageView.userInteractionEnabled = YES;}
然后在實現部分找到 tap:
方法,并添加下面的代碼:
- (IBAction)tap:(id)sender{ // 添加提示框 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Save QRCode?" message:@"The QRCode will be saved in Camera Roll album." preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *saveAction = [UIAlertAction actionWithTitle:@"Save" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // 保存二維碼圖像 [self saveQRCodeImage]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:saveAction]; [alertController addAction:cancelAction]; [self presentViewController:alertController animated:YES completion:nil];}
這時編譯器會有紅色警告提示 saveQRCodeImage
方法未聲明。接下來我們就在實現部分添加該方法來實現二維碼的保存:
- (void)saveQRCodeImage{ // 繪制圖像 UIGraphicsBeginImageContext(self.imageView.image.size); [self.imageView.image drawInRect:self.imageView.bounds]; self.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // 保存圖像 UIImageWriteToSavedPhotosAlbum(self.imageView.image, nil, nil, nil);}
值得說明的是,這里的 self.imageView.image
是從CIImage對象轉換來的,是保存不到相冊的,需要先在圖形上下文中繪制一下,生成一個新的圖像,然后才能保存。
此時,運行程序,點擊生成的二維碼圖像你會看到下面的效果:
但是點擊 Save 按鈕時,程序會崩潰,在控制臺會看到下面的消息:
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryAddUsageDescription key with a string value explaining to the user how the app uses this data.
這是因為iOS要求應用程序開發者在允許訪問相冊之前要先獲得用戶的許可。為此,我們必須在 Info.plist
文件中添加名為 NSPhotoLibraryAddUsageDescription
的鍵,并為其添加相應的描述:
問題修復完成!再次運行,點擊保存按鈕,打開相冊應用,你會看到剛才保存的二維碼:
到目前為止,我們的工作已基本完成,但是為了更完美一些,最好是在點擊保存按鈕之后能夠收到是否保存成功的反饋。所以需要在 saveQRCodeImage
方法中更改 UIImageWriteToSavedPhotosAlbum
函數的參數:
- (void)saveQRCodeImage{ ... // 保存圖像 UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);}
然后添加 image:didFinishSavingWithError:contextInfo:
方法,并在該方法內創建alert View來顯示二維碼的保存狀態:
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{ NSString *title; NSString *message; if (!error) { message = @"The QRCode image saved successfully."; } else { message = @"The QRCode image saved unsuccessfully, please try again later."; } // 使用alert view顯示二維碼保存狀態 UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; [alert addAction:action]; [self presentViewController:alert animated:YES completion:nil];}
運行程序,測試一下效果:
至此,我們的二維碼生成器已經全部完成,如果需要完整代碼,可以下載 QRCodeGeneratorDemo 查看。
3 參考資料
CIFilter - Core Image | Apple Developer Documentation
Building a QR Code Generator with Core Image Filters
HOW TO SAVE/LOAD IMAGE/VIDEOS FROM CAMERA ROLL – XCODE IOS
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答