溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

iOS 定制多樣式二維碼

發(fā)布時間:2020-10-04 16:15:28 來源:腳本之家 閱讀:197 作者:sindriLin 欄目:移動開發(fā)

二維碼/條形碼是按照某種特定的幾何圖形按一定規(guī)律在平臺(一維/二維方向上)分布的黑白相間的圖形紀(jì)錄符號信息。使用若干個與二進(jìn)制對應(yīng)的幾何形體來表示文字?jǐn)?shù)值信息。

iOS 定制多樣式二維碼

最常見的二維碼功能包括信息獲取、網(wǎng)站跳轉(zhuǎn)、電商交易、手機支付等等,其擁有密度小、信息容量大、容錯能力強、成本低、制作難度低等優(yōu)點。在移動開發(fā)中,二維碼的地位也越來越重要,掌握二維碼的基本操作是重要的本領(lǐng)之一。

在iOS7之后,蘋果自身集成了二維碼的生成和讀取功能。生成二維碼包括以下步驟

  • 導(dǎo)入CoreImage/CoreImage.h頭文件
  • 使用CIFilter濾鏡類生成二維碼
  • 對生成的二維碼進(jìn)行加工,使其更清晰

除了上述三個步驟之外,我們還可以對二維碼進(jìn)行進(jìn)一步的拓展處理

  • 自定義二維碼圖案顏色
  • 在二維碼中心插入圓角小圖片
  • 在圓角圖片下面加上一層圓角白色圖片

二維碼生成

碼農(nóng)們生產(chǎn)代碼的同時永遠(yuǎn)不要忘記盡可能的復(fù)用,那么為了實現(xiàn)這種目的,本文的代碼通過類別拓展UIImage的方法來完成。我們先聲明并實現(xiàn)一個類方法用來接收二維碼存儲數(shù)據(jù)以及二維碼尺寸的方法:

+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress codeSize: (CGFloat)codeSize {
  if (!networkAddress|| (NSNull *)networkAddress == [NSNull null]) { 
    return nil;  
  }
  codeSize = [self validateCodeSize: codeSize]; 
  CIImage * originImage = [self createQRFromAddress: networkAddress];
  UIImage * result = [UIImage imageWithCIImage: originImage]; 
  return result;
}

在上面的代碼里面,我們總共做了四件事情:驗證存儲信息的有效性;驗證二維碼尺寸的合理大?。皇褂么鎯π畔⑸啥S碼;將二維碼轉(zhuǎn)成UIImage返回。這些方法的實現(xiàn)分別如下:

/*! 驗證二維碼尺寸合法性*/
+ (CGFloat)validateCodeSize: (CGFloat)codeSize
{
  codeSize = MAX(160, codeSize);
  codeSize = MIN(CGRectGetWidth([UIScreen mainScreen].bounds) - 80, codeSize);
  return codeSize;
}

/*! 利用系統(tǒng)濾鏡生成二維碼圖*/
+ (CIImage *)createQRFromAddress: (NSString *)networkAddress
{
  NSData * stringData = [networkAddress dataUsingEncoding: NSUTF8StringEncoding];
  CIFilter * qrFilter = [CIFilter filterWithName: @"CIQRCodeGenerator"];
  [qrFilter setValue: stringData forKey: @"inputMessage"];
  [qrFilter setValue: @"H" forKey: @"inputCorrectionLevel"];
  return qrFilter.outputImage;
}

/! 驗證二維碼尺寸合法性/
- (CGFloat)validateCodeSize: (CGFloat)codeSize
{
  codeSize = MAX(160, codeSize);
  codeSize = MIN(CGRectGetWidth([UIScreen mainScreen].bounds) - 80, codeSize);
  return codeSize;
}

/! 利用系統(tǒng)濾鏡生成二維碼圖/
- (CIImage *)createQRFromAddress: (NSString *)networkAddress
{
  NSData * stringData = [networkAddress dataUsingEncoding: NSUTF8StringEncoding];
  CIFilter * qrFilter = [CIFilter filterWithName: @"CIQRCodeGenerator"];
  [qrFilter setValue: stringData forKey: @"inputMessage"];
  [qrFilter setValue: @"H" forKey: @"inputCorrectionLevel"];
  return qrFilter.outputImage;
}

ps:對于CIFilter想要更進(jìn)一步了解,可以在xcode中使用快捷鍵shift+command+0打開文檔,然后搜索core image filter reference獲取更多濾鏡的使用方法,這些濾鏡可以用來實現(xiàn)類似美圖秀秀的修圖功能。

上面的代碼生成了一個粗略的二維碼圖,我們需要對圖片再進(jìn)行一次處理,使其清晰化。因為,我們需要另外一個類別方法:

/! 對圖像進(jìn)行清晰化處理/
- (UIImage *)excludeFuzzyImageFromCIImage: (CIImage *)image size: (CGFloat)size
{
  CGRect extent = CGRectIntegral(image.extent);
  CGFloat scale = MIN(size / CGRectGetWidth(extent), size / CGRectGetHeight(extent));
  size_t width = CGRectGetWidth(extent) * scale; 
  size_t height = CGRectGetHeight(extent) * scale;

  //創(chuàng)建灰度色調(diào)空間
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
  CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, colorSpace, (CGBitmapInfo)kCGImageAlphaNone);
  CIContext * context = [CIContext contextWithOptions: nil];
  CGImageRef bitmapImage = [context createCGImage: image fromRect: extent];
  CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
  CGContextScaleCTM(bitmapRef, scale, scale);
  CGContextDrawImage(bitmapRef, extent, bitmapImage);

  CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
  CGContextRelease(bitmapRef);
  CGImageRelease(bitmapImage);
  CGColorSpaceRelease(colorSpace);
  return [UIImage imageWithCGImage: scaledImage];
}

那么這時候,我們把+(UIImage *)imageOfQRFromURL: codeSize:的最后改成

UIImage * result =[self excludeFuzzyImageFromCIImage: originImage size: codeSize];

示例完成后生成的二維碼效果圖如下:

iOS 定制多樣式二維碼

二維碼拓展

單一的黑白色二維碼并不一定總能滿足開發(fā)的需求或者說領(lǐng)導(dǎo)的需求。好比現(xiàn)在的應(yīng)用很多功能界面上都在朝著微信學(xué)習(xí),這就包括了更多色彩,更多樣式的二維碼。本文將從顏色、二維碼中心小圖案這兩點入手講解如何制作類似微信生成我的二維碼的樣式。

自定義二維碼顏色的實現(xiàn)思路是,遍歷生成的二維碼的像素點,將其中為白色的像素點填充為透明色,非白色則填充為我們自定義的顏色。但是,這里的白色并不單單指純白色,rgb值高于一定數(shù)值的灰色我們也可以視作白色處理。在這里我對白色的定義為rgb值高于0xd0d0d0的顏色值為白色,這個值并不是確定的,大家可以自己設(shè)置?;陬伾脑O(shè)置,我們將原有生成二維碼的方法接口改成這樣:

+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress codeSize: (CGFloat)codeSize red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue 
{ 
  if (!networkAddress || (NSNull *)networkAddress == [NSNull null]) { return nil; }
  /** 顏色不可以太接近白色*/
  NSUInteger rgb = (red << 16) + (green << 8) + blue;
  NSAssert((rgb & 0xffffff00) <= 0xd0d0d000, @"The color of QR code is two close to white color than it will diffculty to scan");

  codeSize = [self validateCodeSize: codeSize]; 
  CIImage * originImage = [self createQRFromAddress: networkAddress];
  UIImage * progressImage = [self excludeFuzzyImageFromCIImage: originImage size: codeSize];   //到了這里二維碼已經(jīng)可以進(jìn)行掃描了
  UIImage * effectiveImage = [self imageFillBlackColorAndTransparent: progressImage red: red green: green blue: blue]; //進(jìn)行顏色渲染后的二維碼
  return effectiveImage;
}

相較于前面的代碼,多了兩個步驟:判斷rgb的有效值;對二維碼進(jìn)行顏色渲染。顏色渲染的過程包括獲取圖像的位圖上下文、像素替換、二進(jìn)制圖像轉(zhuǎn)換等操作,具體代碼如下:

/*! 對生成二維碼圖像進(jìn)行顏色填充*/
+ (UIImage *)imageFillBlackColorAndTransparent: (UIImage *)image red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue {

 const int imageWidth = image.size.width;
 const int imageHeight = image.size.height;
 size_t bytesPerRow = imageWidth * 4;
 uint32_t * rgbImageBuf = (uint32_t *)malloc(bytesPerRow * imageHeight);

 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
 CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
 CGContextDrawImage(context, (CGRect){(CGPointZero), (image.size)}, image.CGImage);

 //遍歷像素
 int pixelNumber = imageHeight * imageWidth;
 [self fillWhiteToTransparentOnPixel: rgbImageBuf pixelNum: pixelNumber red: red green: green blue: blue];
 CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow, ProviderReleaseData);
 CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace, kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider, NULL, true, kCGRenderingIntentDefault);

 UIImage * resultImage = [UIImage imageWithCGImage: imageRef]; 
 CGImageRelease(imageRef);
 CGColorSpaceRelease(colorSpace);
 CGContextRelease(context);
 return resultImage;
}

/! 遍歷所有像素點進(jìn)行顏色替換/
+ (void)fillWhiteToTransparentOnPixel: (uint32_t *)rgbImageBuf pixelNum: (int)pixelNum red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue {
uint32_t * pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++) {
    if ((*pCurPtr & 0xffffff00) < 0xd0d0d000) {
      uint8_t * ptr = (uint8_t *)pCurPtr;
      ptr[3] = red;
      ptr[2] = green;
      ptr[1] = blue;
    } else {
      //將白色變成透明色
      uint8_t * ptr = (uint8_t *)pCurPtr;
      ptr[0] = 0;
    }
  }
}

void ProviderReleaseData(void * info, const void * data, size_t size) {
  free((void *)data);
}

ps:在修改代碼之前,應(yīng)該想清楚是否需要刪除原有代碼。類似這種二維碼的擴展,舊的二維碼生成接口可以留下來,然后在其中調(diào)用多參數(shù)的全能構(gòu)造器Designated Initializer。

iOS 定制多樣式二維碼

這時候距離微信還差一小步,我們要在二維碼的中心位置插入我們的小頭像,最直接的方式是加載完我們的頭像后,直接drawInRect:這種實現(xiàn)方法是正確的,但是在我們畫上去之前,我們還需要對圖像進(jìn)行圓角處理。(省事的可能直接用imageView加載頭像,然后設(shè)置頭像的cornerRadius,這個也能實現(xiàn)效果)。

到了這個時候,我們需要一個更多參數(shù)的二維碼生成方法接口了,這次新增的參數(shù)應(yīng)該包括插入圖片、圓角半徑這些參數(shù),因此方法如下:

+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress codeSize: (CGFloat)codeSize red: (NSUInteger)red green: (NSUInteger)green blue: (NSUInteger)blue insertImage: (UIImage *)insertImage roundRadius: (CGFloat)roundRadius {

  if (!networkAddress || (NSNull *)networkAddress == [NSNull null]) { return nil; }
  /** 顏色不可以太接近白色*/
  NSUInteger rgb = (red << 16) + (green << 8) + blue;
  NSAssert((rgb & 0xffffff00) <= 0xd0d0d000, @"The color of QR code is two close to white color than it will diffculty to scan");
  codeSize = [self validateCodeSize: codeSize];

  CIImage * originImage = [self createQRFromAddress: networkAddress];
  UIImage * progressImage = [self excludeFuzzyImageFromCIImage: originImage size: codeSize];   //到了這里二維碼已經(jīng)可以進(jìn)行掃描了
  UIImage * effectiveImage = [self imageFillBlackColorAndTransparent: progressImage red: red green: green blue: blue]; //進(jìn)行顏色渲染后的二維碼
  return [self imageInsertedImage: effectiveImage insertImage: insertImage radius: roundRadius];
}

這次的生成方法同樣也只需要進(jìn)行一次額外的調(diào)用方法操作,在插入圖片的時候我們需要注意,類似微信的圖中圖二維碼中間的小頭像是有一個圓角的白色邊緣的,這個邊緣的加入讓頭像顯示的更加自然。那么要完成這個效果,我額外在項目中加入了一張白色背景的小圖,同樣對這張圖片進(jìn)行圓角化處理,然后加在頭像的下面。作為頭像下方的白色背景圖像尺寸應(yīng)該大于頭像圖。制作畫中畫效果的具體實現(xiàn)如下:

/! 在二維碼原圖中心位置插入圓角圖像/
+ (UIImage *)imageInsertedImage: (UIImage *)originImage insertImage: (UIImage *)insertImage radius: (CGFloat)radius
{
  if (!insertImage) { return originImage; }
  insertImage = [UIImage imageOfRoundRectWithImage: insertImage size: insertImage.size radius: radius];
  UIImage * whiteBG = [UIImage imageNamed: @"whiteBG"];
  whiteBG = [UIImage imageOfRoundRectWithImage: whiteBG size: whiteBG.size radius: radius];

  //白色邊緣寬度
  const CGFloat whiteSize = 2.f;
  CGSize brinkSize = CGSizeMake(originImage.size.width / 4, originImage.size.height / 4);
  CGFloat brinkX = (originImage.size.width - brinkSize.width) * 0.5;
  CGFloat brinkY = (originImage.size.height - brinkSize.height) * 0.5;
  CGSize imageSize = CGSizeMake(brinkSize.width - 2 * whiteSize, brinkSize.height - 2 * whiteSize);

  CGFloat imageX = brinkX + whiteSize;
  CGFloat imageY = brinkY + whiteSize;
  UIGraphicsBeginImageContext(originImage.size);
  [originImage drawInRect: (CGRect){ 0, 0, (originImage.size) }];
  [whiteBG drawInRect: (CGRect){ brinkX, brinkY, (brinkSize) }];
  [insertImage drawInRect: (CGRect){ imageX, imageY, (imageSize) }];

  UIImage * resultImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return resultImage;
}

- (UIImage *)imageOfRoundRectWithImage: (UIImage *)image size: (CGSize)size radius: (CGFloat)radius
{
  if (!image) { return nil; }
  const CGFloat width = size.width;
  const CGFloat height = size.height;
  radius = MAX(5.f, radius);
  radius = MIN(10.f, radius);

  UIImage * img = image; 
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedFirst);
  CGRect rect = CGRectMake(0, 0, width, height);

  //繪制圓角
  CGContextBeginPath(context);
  addRoundRectToPath(context, rect, radius, img.CGImage);
  CGImageRef imageMasked = CGBitmapContextCreateImage(context);
  img = [UIImage imageWithCGImage: imageMasked];

  CGContextRelease(context);
  CGColorSpaceRelease(colorSpace);
  CGImageRelease(imageMasked);
  return img;
}

ps:圖像繪制圓角是通過在圖像上下文中畫出圓角矩形的路徑,然后進(jìn)行裁剪,這樣就能實現(xiàn)圖片的圓角化。

iOS 定制多樣式二維碼

在代碼中,對中心位置的頭像限制尺寸為二維碼的四分之一,這個尺寸下的頭像不失清晰度,而且圖片尺寸也不至于遮蓋了二維碼的存儲數(shù)據(jù)。上面的方法都可以在頭文件中開發(fā)方法接口使用,這將實現(xiàn)這些代碼的復(fù)用。另外,所有本文中寫到的生成二維碼的接口都應(yīng)該在頭文件中聲明,并且在其實現(xiàn)中調(diào)用全能方法(不應(yīng)當(dāng)僅僅是構(gòu)造器需要遵循Designated Initializer的原則):

+ (UIImage *)imageOfQRFromURL: (NSString *)networkAddress 
{
  return [self imageOfQRFromURL: networkAddress codeSize: 100.0f red: 0 green: 0 blue: 0 insertImage: nil roundRadius: 0.f];
}

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持億速云!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI