iOS使用CIFilter生成二維碼
二維碼(Quick Response Code,簡(jiǎn)稱QR Code)是由水平和垂直兩個(gè)方向上的線條設(shè)計(jì)而成的一種二維條形碼(barcode)??梢跃幋a網(wǎng)址、電話號(hào)碼、文本等內(nèi)容,能夠存儲(chǔ)大量的數(shù)據(jù)信息。自iOS 7以來(lái),二維碼的生成和讀取只需要使用Core Image框架和AVFoundation框架就能輕松實(shí)現(xiàn)。在這里,我們主要介紹二維碼的生成。關(guān)于二維碼的讀取,在 使用AVFoundation讀取二維碼 文章中有詳細(xì)介紹。
1 二維碼的生成
生成一個(gè)二維碼也就是根據(jù)提供的數(shù)據(jù)內(nèi)容轉(zhuǎn)換成一張二維碼圖像。從iOS 7開(kāi)始,我們只需要使用CIFilter中的CIQRCodeGenerator就可以輕易實(shí)現(xiàn)。只不過(guò)這樣生成的二維碼圖像是一個(gè)CIImage對(duì)象,如果要在圖像視圖中顯示,需要將其轉(zhuǎn)換為UIImage對(duì)象。具體步驟如下:
①、使用名為 CIQRCodeGenerator 的過(guò)濾器創(chuàng)建一個(gè)CIFilter對(duì)象。
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]
②、為CIFilter對(duì)象設(shè)置 inputMessage 和 inputCorrectionLevel 參數(shù)。
- inputMessage :是一個(gè)NSData對(duì)象,用于表示被編碼的數(shù)據(jù)。對(duì)于字符串或者URL,需要使用NSISOLatin1StringEncoding字符串編碼將其轉(zhuǎn)換為NSData對(duì)象。要注意的是,NSISOLatin1StringEncoding編碼對(duì)于中文或表情無(wú)法生成,需要的話可以使用NSUTF8StringEncoding 替換。
- inputCorrectionLevel :是一個(gè)NSString對(duì)象,通常使用單個(gè)字母來(lái)指定糾錯(cuò)率,默認(rèn)值是 M 。該參數(shù)控制輸出圖像中編碼的附加數(shù)據(jù)量以提供糾錯(cuò)。其糾錯(cuò)率越高,輸出的圖像越大,同時(shí)也允許代碼的更大區(qū)域被破壞或模糊。通常有 L 、 M 、 Q 、 H 這四種可能的糾正模式,分別代表了7%、15%、25%、30%的錯(cuò)誤恢復(fù)能力。
③、使用CIFilter對(duì)象的 outputImage 屬性獲取生成的二維碼圖像
CIImage *outputImage = filter.outputImage;
④、對(duì)生成的二維碼圖像進(jìn)行縮放。
由于生成的二維碼圖像尺寸一般都比較小,為了避免模糊,通常需要對(duì)它進(jìn)行縮放以適應(yīng)圖像視圖的大小。其縮放比例一般為圖像視圖寬度(或高度)與二維碼圖像寬度(或高度)的比值。
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)];
⑤、將二維碼圖像轉(zhuǎn)換為UIImage對(duì)象。
imageView.image = [UIImage imageWithCIImage:transformedImage];
2 應(yīng)用示例
下面,我們就做一個(gè)如下圖所示的二維碼生成器:

其中主要實(shí)現(xiàn)的功能有:
- 生成和刪除二維碼。
- 通過(guò)滑動(dòng)條對(duì)二維碼圖像進(jìn)行縮放。
- 將二維碼保存到相冊(cè)。
2.1 創(chuàng)建項(xiàng)目
打開(kāi) Xcode ,創(chuàng)建一個(gè)新的項(xiàng)目( File\New\Project.. .),選擇 iOS 一欄下的 Application 中的 Single View Application 模版,然后點(diǎn)擊 Next ,填寫項(xiàng)目選項(xiàng)。在 Product Name 中填寫 QRCodeGeneratorDemo ,選擇 Objective-C 語(yǔ)言,點(diǎn)擊 Nex t,選擇文件位置,并單擊 Create 創(chuàng)建項(xiàng)目。
2.2 構(gòu)建界面
打開(kāi) Main.storyboard 文件,在當(dāng)前控制器中嵌入導(dǎo)航控制器,并添加標(biāo)題 QR Code Generator :

在視圖控制器中添加文本框、按鈕、圖像視圖等,布局如下:

其中各元素及作用:
- Text Field:用于輸入想要轉(zhuǎn)換為二維碼的數(shù)據(jù)內(nèi)容,包括文本或URL字符串。
- Button:在這里具有雙重作用,用于生成和清除二維碼。
- Image View:用于顯示生成的二維碼圖像。
- Slider:用來(lái)縮放生成的二維碼圖像。
打開(kāi)輔助編輯器,將storyboard中的元素連接到代碼中:
2.3 添加代碼
2.3.1 生成二維碼圖像
由于使用CIFilter對(duì)象生成的二維碼圖像將是一個(gè)CIImage對(duì)象,所以需要先在 ViewController.m 文件的接口部分聲明一個(gè)CIImage對(duì)象的屬性:
@property (strong, nonatomic) CIImage *qrcodeImage;
然后在實(shí)現(xiàn)部分添加 generateQRCodeImage 方法及代碼:
- (void)generateQRCodeImage
{
NSData *data = [self.textField.text dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:NO];
// 創(chuàng)建并設(shè)置CIFilter對(duì)象
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setValue:data forKey:@"inputMessage"];
[filter setValue:@"Q" forKey:@"inputCorrectionLevel"];
// 獲取生成的CIImage對(duì)象
self.qrcodeImage = filter.outputImage;
// 轉(zhuǎn)換成UIImage對(duì)象,并顯示在圖像視圖中
self.imageView.image = [UIImage imageWithCIImage:self.qrcodeImage];
}
在 clickButton: 方法中調(diào)用該方法:
- (IBAction)clickButton:(id)sender
{
[self generateQRCodeImage];
}
此時(shí),運(yùn)行程序,在文本框中輸入內(nèi)容,點(diǎn)擊按鈕就可以看到生成的二維碼:

仔細(xì)的話你會(huì)發(fā)現(xiàn)這里的二維碼比較模糊,這是由于生成的二維碼尺寸較小,在imageView中顯示時(shí)被拉伸導(dǎo)致的。下面我們就通過(guò)調(diào)整二維碼的縮放來(lái)解決圖像模糊的問(wèn)題。
修改 generateQRCodeImage 方法中的代碼如下:
- (void)generateQRCodeImage
{
...
// 獲取生成的CIImage對(duì)象
self.qrcodeImage = filter.outputImage;
// 縮放CIImage對(duì)象
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)];
// 將調(diào)整后的CIImage對(duì)象轉(zhuǎn)換成UIImage對(duì)象,并顯示在圖像視圖中
self.imageView.image = [UIImage imageWithCIImage:transformedImage];
}
再次運(yùn)行,你會(huì)看到一張清晰的二維碼:
2.3.2 刪除二維碼圖像
在這個(gè)demo中,按鈕具有雙重作用:即生成二維碼和刪除二維碼。因此需要在 clickButton: 方法中先判斷二維碼圖像是否存在:
- 如果不存在,點(diǎn)擊按鈕生成一張二維碼圖像,按鈕的標(biāo)題變?yōu)? Clear "。
- 如果存在,點(diǎn)擊按鈕刪除二維碼圖像,按鈕的標(biāo)題變?yōu)? Generate "。
修改 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 方法并實(shí)現(xiàn)它:
- (void)clearQRCodeImage
{
self.imageView.image = nil;
self.qrcodeImage = nil;
self.textField.text = nil;
}
至此,二維碼的生成和刪除已基本完成,但為了有良好的體驗(yàn),我們還需要考慮下面的情況:
- 在文本框未輸入的情況下點(diǎn)擊按鈕是否生成二維碼。
- 切換按鈕時(shí)文本框的響應(yīng)狀態(tài)以及鍵盤出現(xiàn)與消失。
- 滑動(dòng)條的顯示和隱藏。
于是,在 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;
}
最后,考慮到程序啟動(dòng)時(shí)滑動(dòng)條不應(yīng)該顯示(滑動(dòng)條只在生成二維碼時(shí)出現(xiàn)),還需要在 viewDidLoad 方法中設(shè)置其 hidden 屬性:
- (void)viewDidLoad
{
[super viewDidLoad];
self.slider.hidden = YES;
}
可以再次運(yùn)行試下。
2.3.3 縮放二維碼圖像
縮放顯示的二維碼主要是通過(guò)拖動(dòng)滑動(dòng)條縮放image View來(lái)完成的。在實(shí)現(xiàn)部分找到 changeScale: 方法,并添加代碼下面的代碼即可:
- (IBAction)changeScale:(id)sender
{
self.imageView.transform = CGAffineTransformMakeScale((CGFloat)self.slider.value, (CGFloat)self.slider.value);
}
需要注意的是, self.slider.value 是 float 類型,而 CGAffineTransformMakeScale 方法的參數(shù)是 CGFloat 類型,因此在上面的代碼中進(jìn)行了類型轉(zhuǎn)換。
2.3.4 保存二維碼圖像
將生成的二維碼圖片保存到相機(jī)膠卷相冊(cè)主要是使用 UIImageWriteToSavedPhotosAlbum 函數(shù)來(lái)實(shí)現(xiàn)的。其完整聲明如下:
void UIImageWriteToSavedPhotosAlbum(UIImage *image, id completionTarget, SEL completionSelector, void *contextInfo);
其中各參數(shù)及含義:
image :表示要保存到相冊(cè)的圖像。
completionTarget :可選的參數(shù),表示圖片保存后,調(diào)用完成選擇器(completionSelector)的對(duì)象。
completionSelector :可選的方法,表示圖片保存后,completionTarget對(duì)象要調(diào)用的方法(即回調(diào)方法)。該方法應(yīng)符合以下簽名:
- (void)image:(UIImage *)image
didFinishSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo;
contextInfo :可選的參數(shù),用于提供一個(gè)上下文信息以通過(guò)參數(shù)傳遞給completionSelector。
在這里我們的大致思路是:首先在imageView中添加單擊手勢(shì),當(dāng)用戶點(diǎn)擊二維碼圖像時(shí)會(huì)彈出一個(gè)提示框詢問(wèn)是否保存,如果用戶點(diǎn)擊保存按鈕,那么就將二維碼保存到相冊(cè)中。下面是具體實(shí)現(xiàn):
打開(kāi) Main.storyboard 文件,從對(duì)象庫(kù)中找到點(diǎn)擊手勢(shì)(Tap Gesture Recognizer),將其添加到視圖控制器的Image View上,完成之后會(huì)在控制器的頂部看到它:

打開(kāi)輔助編輯器,將其連接到代碼中:

為了使image View能響應(yīng)手勢(shì)操作,需要在 viewDidLoad 中設(shè)置image View的 userInteractionEnabled 屬性:
- (void)viewDidLoad
{
...
self.imageView.userInteractionEnabled = YES;
}
然后在實(shí)現(xiàn)部分找到 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];
}
這時(shí)編譯器會(huì)有紅色警告提示 saveQRCodeImage 方法未聲明。接下來(lái)我們就在實(shí)現(xiàn)部分添加該方法來(lái)實(shí)現(xiàn)二維碼的保存:
- (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);
}
值得說(shuō)明的是,這里的 self.imageView.image 是從CIImage對(duì)象轉(zhuǎn)換來(lái)的,是保存不到相冊(cè)的,需要先在圖形上下文中繪制一下,生成一個(gè)新的圖像,然后才能保存。
此時(shí),運(yùn)行程序,點(diǎn)擊生成的二維碼圖像你會(huì)看到下面的效果:

但是點(diǎn)擊 Save 按鈕時(shí),程序會(huì)崩潰,在控制臺(tái)會(huì)看到下面的消息:
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.
這是因?yàn)閕OS要求應(yīng)用程序開(kāi)發(fā)者在允許訪問(wèn)相冊(cè)之前要先獲得用戶的許可。為此,我們必須在 Info.plist 文件中添加名為 NSPhotoLibraryAddUsageDescription 的鍵,并為其添加相應(yīng)的描述:

問(wèn)題修復(fù)完成!再次運(yùn)行,點(diǎn)擊保存按鈕,打開(kāi)相冊(cè)應(yīng)用,你會(huì)看到剛才保存的二維碼:

到目前為止,我們的工作已基本完成,但是為了更完美一些,最好是在點(diǎn)擊保存按鈕之后能夠收到是否保存成功的反饋。所以需要在 saveQRCodeImage 方法中更改 UIImageWriteToSavedPhotosAlbum 函數(shù)的參數(shù):
- (void)saveQRCodeImage
{
...
// 保存圖像
UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
然后添加 image:didFinishSavingWithError:contextInfo: 方法,并在該方法內(nèi)創(chuàng)建alert View來(lái)顯示二維碼的保存狀態(tài):
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
NSString *title;
NSString *message;
if (!error) {
title = @"Success!";
message = @"The QRCode image saved successfully.";
}
else {
title = @"Failed!";
message = @"The QRCode image saved unsuccessfully, please try again later.";
}
// 使用alert view顯示二維碼保存狀態(tài)
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];
}
運(yùn)行程序,測(cè)試一下效果:

至此,我們的二維碼生成器已經(jīng)全部完成,如果需要完整代碼,可以下載 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
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS中使用NSURLConnection處理HTTP同步與異步請(qǐng)求
NSURLConnection的作用現(xiàn)在已經(jīng)基本被NSURLSession所取代,所以我們簡(jiǎn)單了解下iOS中使用NSURLConnection處理HTTP同步與異步請(qǐng)求的方法即可:2016-07-07
理解iOS多線程應(yīng)用的開(kāi)發(fā)以及線程的創(chuàng)建方法
這篇文章主要介紹了理解iOS多線程應(yīng)用的開(kāi)發(fā)以及線程的創(chuàng)建方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-11-11
iOS 點(diǎn)擊圖片放大效果的實(shí)現(xiàn)
本篇文章主要介紹了iOS 點(diǎn)擊圖片放大效果的實(shí)現(xiàn),這種效果一般在微博,微信朋友圈中比較常見(jiàn),有興趣的可以了解一下。2017-01-01
Android中g(shù)etActivity()為null的解決辦法
在Android開(kāi)發(fā)的時(shí)候可能遇過(guò)出現(xiàn)getActivity()出現(xiàn)null的時(shí)候?qū)е鲁绦驁?bào)出空指針異常,那么遇到這種情況改如何解決,下面跟著小編一起去看看。2016-08-08
iOS 利用動(dòng)畫和貝塞爾實(shí)現(xiàn)咻咻效果
這篇文章主要介紹了iOS 利用動(dòng)畫和貝塞爾實(shí)現(xiàn)咻咻效果的相關(guān)資料,需要的朋友可以參考下2016-09-09
IOS 屏幕適配方案實(shí)現(xiàn)縮放window的示例代碼
這篇文章主要介紹了IOS 屏幕適配方案實(shí)現(xiàn)縮放window的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
iOS?RN啟動(dòng)中管理Native?Module詳解
這篇文章主要為大家介紹了iOS?RN啟動(dòng)中?Native?Module?是如何被管理的,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

