swift 个性化二维码

前言:最近项目里面有一个需求,要改变定位角标的颜色
大至如图:
在这里插入图片描述

于是整理了一下
可以实现以下几个功能:
  • 普通二维码
  • 前景色,背景色
  • 添加icon
  • 定位角标(里,外)

直接放代码,大家可以根据需求自己封装

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,这个就是参照他写的,在此非常的感谢他😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值