前言:最近项目里面有一个需求,要改变定位角标的颜色
大至如图:
于是整理了一下
可以实现以下几个功能:
直接放代码,大家可以根据需求自己封装
import UIKit
let outerPositionPathOriginLength: CGFloat = 6.0;
let outerPositionTileOriginWidth: CGFloat = 7.0;
enum QRPosition {
case TopLeft
case TopRight
case BottomLeft
case Center
case QuietZone
}
class QRCodeView: UIImageView {
/// 前景色
let foreColor = UIColor.black
/// 背景色
let backColor = UIColor.white
/// logo 的线宽
let borderWidth: CGFloat = 50
/// logo线的颜色
let borderColor = UIColor.white
/// logo的宽高
let logoSize: CGFloat = 80
/// 定位角标里面的颜色
let interAngle = UIColor.red
/// 定位角标外面的颜色
let outerAngle = UIColor.blue
var outPutImage: CIImage = CIImage()
init(QRCodeStr: String) {
super.init(frame: CGRect(x: 50, y: 200, width: 300, height: 300))
/// 获取二维码原图
outPutImage = generateQRCodeFilter(text: QRCodeStr)
/// 前景色 背景色 二维码
// self.image = generateColorCodeFilter(qrImage: outPutImage)
/// 高清二维码
let highImage = setupHighDefinitionUIImage(outPutImage, size: self.frame.size.width)
/// 添加头像
let headImage = addPicture(qrCodeImage: highImage, image: UIImage(named: "head") ?? UIImage())
/// 修改定位角标
let angleImage = changeOrientationAngle(image: headImage)
self.image = angleImage
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 添加logo
func addPicture(qrCodeImage:UIImage, image: UIImage) -> UIImage {
//给头像加一个白色圆边(如果没有这个需求直接忽略)
let image = circleImageWithImage(image, borderWidth: borderWidth, borderColor: borderColor)
//合成图片
let newImage = syntheticImage(qrCodeImage, iconImage: image, width: logoSize, height: logoSize)
return newImage
}
/// 定位角标
func changeOrientationAngle(image: UIImage) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image.size, false, UIScreen.main.scale);
image.draw(in: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.width))
let cifiter = CIFilter(name: "CICrop")
cifiter?.setValue(CIVector(x: 0, y: 0, z: 10, w: 10), forKey: "inputRectangle")
let coreImage = CIImage(color: CIColor(cgColor: interAngle.cgColor))
cifiter?.setValue(coreImage, forKey: "inputImage")
if let filteredImage = cifiter?.outputImage {
/// 里面的框框
let colorImage = UIImage.init(ciImage: filteredImage)
self.changePositionInnerColor(image: colorImage, position: .TopLeft)
self.changePositionInnerColor(image: colorImage, position: .TopRight)
self.changePositionInnerColor(image: colorImage, position: .BottomLeft)
/// 外面的框框
self.changeOuterPositionColor(color: outerAngle, position: .TopLeft)
self.changeOuterPositionColor(color: outerAngle, position: .TopRight)
self.changeOuterPositionColor(color: outerAngle, position: .BottomLeft)
}
guard let newPic = UIGraphicsGetImageFromCurrentImageContext() else { return UIImage() }
UIGraphicsEndImageContext()
return newPic
}
/// 基本二维码
func generateQRCodeFilter(text: String) -> CIImage {
/// 创建二维码滤镜
let qrFilter = CIFilter(name: "CIQRCodeGenerator")
qrFilter?.setDefaults()
qrFilter?.setValue(text.data(using: String.Encoding.utf8), forKey: "inputMessage")
qrFilter?.setValue("H", forKey: "inputCorrectionLevel")
guard let outputImage = qrFilter?.outputImage else { return CIImage() }
return outputImage
}
/// 创建颜色滤镜
func generateColorCodeFilter(qrImage: CIImage) -> UIImage {
/// 创建颜色滤镜
let colorFilter = CIFilter(name: "CIFalseColor")
colorFilter?.setDefaults()
colorFilter?.setValue(qrImage, forKey: "inputImage")
let foreColor = CIColor(cgColor: self.foreColor.cgColor)
let backColor = CIColor(cgColor: self.backColor.cgColor)
colorFilter?.setValue(foreColor, forKey: "inputColor0")
colorFilter?.setValue(backColor, forKey: "inputColor1")
guard let outputImage = colorFilter?.outputImage else { return UIImage() }
// 返回二维码image
let codeImage = UIImage(ciImage: (outputImage.transformed(by: CGAffineTransform(scaleX: 10, y: 10))))
return codeImage
}
/// 获取Version
func fetchVersion() -> CGFloat {
return ((outPutImage.extent.size.width - 21)/4.0 + 1)
}
/// 设置那个定位角标内框
func changePositionInnerColor(image: UIImage, position: QRPosition) {
let rect = self.innerPositionRectWidth(width: self.frame.size.width, version: self.fetchVersion(), postion: position)
image.draw(in: rect)
}
/// 设置那个定位角标外框
func changeOuterPositionColor(color: UIColor, position: QRPosition) {
let path = self.outerPositionPathWidth(width: self.frame.size.width, version: self.fetchVersion(), position: position)
color.setStroke()
path.stroke()
}
//image: 二维码 iconImage:头像图片 width: 头像的宽 height: 头像的宽
func syntheticImage(_ image: UIImage, iconImage:UIImage, width: CGFloat, height: CGFloat) -> UIImage{
//开启图片上下文
UIGraphicsBeginImageContext(image.size)
//绘制背景图片
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
let x = (image.size.width - width) * 0.5
let y = (image.size.height - height) * 0.5
iconImage.draw(in: CGRect(x: x, y: y, width: width, height: height))
//取出绘制好的图片
let newImage = UIGraphicsGetImageFromCurrentImageContext()
//关闭上下文
UIGraphicsEndImageContext()
//返回合成好的图片
if let newImage = newImage {
return newImage
}
return UIImage()
}
//MARK: - 生成高清的UIImage
func setupHighDefinitionUIImage(_ image: CIImage, size: CGFloat) -> UIImage {
let integral: CGRect = image.extent.integral
let proportion: CGFloat = min(size/integral.width, size/integral.height)
let width = integral.width * proportion
let height = integral.height * proportion
let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceGray()
let bitmapRef = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: 0)!
let context = CIContext(options: nil)
let bitmapImage: CGImage = context.createCGImage(image, from: integral)!
bitmapRef.interpolationQuality = CGInterpolationQuality.none
bitmapRef.scaleBy(x: proportion, y: proportion);
bitmapRef.draw(bitmapImage, in: integral);
let image: CGImage = bitmapRef.makeImage()!
return UIImage(cgImage: image)
}
/// logo 生成边框
func circleImageWithImage(_ sourceImage: UIImage, borderWidth: CGFloat, borderColor: UIColor) -> UIImage {
let imageWidth = sourceImage.size.width + 2 * borderWidth
let imageHeight = sourceImage.size.height + 2 * borderWidth
UIGraphicsBeginImageContextWithOptions(CGSize(width: imageWidth, height: imageHeight), false, 0.0)
UIGraphicsGetCurrentContext()
let radius = (sourceImage.size.width < sourceImage.size.height ? sourceImage.size.width:sourceImage.size.height) * 0.5
let bezierPath = UIBezierPath(arcCenter: CGPoint(x: imageWidth * 0.5, y: imageHeight * 0.5), radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
bezierPath.lineWidth = borderWidth
borderColor.setStroke()
bezierPath.stroke()
bezierPath.addClip()
sourceImage.draw(in: CGRect(x: borderWidth, y: borderWidth, width: sourceImage.size.width, height: sourceImage.size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
/// 定位角标内边框
func innerPositionRectWidth(width: CGFloat, version: CGFloat, postion: QRPosition) -> CGRect {
let leftMargin = width * 3 / ((version - 1) * 4 + 21)
let tileWidth = leftMargin
let centerImageWith = width * 7 / ((version - 1) * 4 + 21)
var rect = CGRect(x: leftMargin + 1.5, y: leftMargin + 1.5, width: leftMargin - 3, height: leftMargin - 3)
rect = rect.integral;
rect = rect.insetBy(dx: -1, dy: -1);
var offset: CGFloat = 0.0
switch postion {
case .TopLeft:
break
case .TopRight:
offset = width - tileWidth - leftMargin*2;
rect = rect.offsetBy(dx: offset, dy: 0);
case .BottomLeft:
offset = width - tileWidth - leftMargin * 2;
rect = rect.offsetBy(dx: 0, dy: offset);
case .Center:
rect = CGRect(x: CGPoint.zero.x, y: CGPoint.zero.y, width: centerImageWith, height: centerImageWith)
offset = width/2 - centerImageWith/2;
rect = rect.offsetBy(dx: offset, dy: offset);
default:
rect = .zero;
}
return rect
}
/// 定位角标外边框
func outerPositionPathWidth(width: CGFloat, version: CGFloat, position: QRPosition) -> UIBezierPath {
let zonePathWidth = width/((version - 1) * 4 + 21)
let positionFrameWidth = zonePathWidth * outerPositionPathOriginLength
let topLeftPoint = CGPoint(x: zonePathWidth * 1.5, y: zonePathWidth * 1.5)
var rect = CGRect(x: topLeftPoint.x - 0.2, y: topLeftPoint.y - 0.2, width: positionFrameWidth, height: positionFrameWidth)
rect = rect.integral;
rect = rect.insetBy(dx: 1, dy: 1);
var path: UIBezierPath
var offset: CGFloat = 0.0
switch position {
case .TopLeft:
path = UIBezierPath(rect: rect)
path.lineWidth = zonePathWidth + 1.5
path.lineCapStyle = .square
case .TopRight:
offset = width - positionFrameWidth - topLeftPoint.x * 2
rect = rect.offsetBy(dx: offset, dy: 0)
path = UIBezierPath(rect: rect)
path.lineWidth = zonePathWidth + 1.5
path.lineCapStyle = .square
case .BottomLeft:
offset = width - positionFrameWidth - topLeftPoint.x * 2
rect = rect.offsetBy(dx: 0, dy: offset)
path = UIBezierPath(rect: rect)
path.lineWidth = zonePathWidth + 1.5
path.lineCapStyle = .square
case .QuietZone:
rect = CGRect(x: zonePathWidth * 0.5, y: zonePathWidth * 0.5, width: width - zonePathWidth, height: width - zonePathWidth)
path = UIBezierPath(rect: rect)
path.lineWidth = zonePathWidth + UIScreen.main.scale;
path.lineCapStyle = .square;
default:
path = UIBezierPath()
}
return path
}
}
调用:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let codeView = QRCodeView(QRCodeStr: "https://www.baidu.com/index.php?tn=monline_3_dg")
self.view.addSubview(codeView)
}
}
如果想知道原理,可以看看二维码的生成规则, 写的很详细。
这个是swift
版的,如果你用OC
版可以在github
上下载 MKQRCode,这个就是参照他写的,在此非常的感谢他😊