You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
8.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import SwiftUI
import CoreImage
import QRCode
// MARK: -
class QRCodeGenerator {
// MARK: - 使Core Image
static func generateBasicQRCode(content: String) -> UIImage {
let data = content.data(using: .utf8)
let qrFilter = CIFilter.qrCodeGenerator()
qrFilter.setValue(data, forKey: "inputMessage")
qrFilter.setValue("H", forKey: "inputCorrectionLevel")
guard let outputImage = qrFilter.outputImage else {
return UIImage(systemName: "qrcode") ?? UIImage()
}
let context = CIContext()
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else {
return UIImage(systemName: "qrcode") ?? UIImage()
}
return UIImage(cgImage: cgImage)
}
// MARK: - 使QRCode
static func generateHighQualityQRCode(
content: String,
styleData: QRCodeStyleData? = nil
) -> UIImage {
do {
let document = try createQRCodeDocument(content: content, styleData: styleData)
let imageData = try document.pngData(dimension: 600)
return UIImage(data: imageData) ?? generateBasicQRCode(content: content)
} catch {
print("生成高质量二维码失败:\(error.localizedDescription)")
return generateBasicQRCode(content: content)
}
}
// MARK: - QRCode
private static func createQRCodeDocument(
content: String,
styleData: QRCodeStyleData?
) throws -> QRCode.Document {
let document = try QRCode.Document(engine: QRCodeEngineExternal())
//
document.utf8String = content
// 使
guard let styleData = styleData else {
// 使
document.design.backgroundColor(CGColor(red: 1, green: 1, blue: 1, alpha: 1)) //
document.design.style.eye = QRCode.FillStyle.Solid(CGColor(red: 0, green: 0, blue: 0, alpha: 1)) //
document.design.style.eyeBackground = CGColor(red: 1, green: 1, blue: 1, alpha: 1) //
document.design.shape.onPixels = QRCode.PixelShape.Square() //
document.design.style.onPixels = QRCode.FillStyle.Solid(CGColor(red: 0, green: 0, blue: 0, alpha: 1)) //
document.design.style.onPixelsBackground = CGColor(red: 1, green: 1, blue: 1, alpha: 1) //
document.design.shape.offPixels = QRCode.PixelShape.Square() //
document.design.style.offPixels = QRCode.FillStyle.Solid(CGColor(red: 1, green: 1, blue: 1, alpha: 1)) //
document.design.style.offPixelsBackground = CGColor(red: 1, green: 1, blue: 1, alpha: 1) //
document.design.shape.eye = QRCode.EyeShape.Square() //
return document
}
//
applyStyleData(styleData, to: document)
return document
}
// MARK: -
private static func applyStyleData(_ styleData: QRCodeStyleData, to document: QRCode.Document) {
//
if let backgroundColor = getColor(from: styleData.backgroundColor) {
document.design.backgroundColor(backgroundColor)
}
//
let foregroundColor = getColor(from: styleData.foregroundColor) ?? CGColor(red: 0, green: 0, blue: 0, alpha: 1)
//
document.design.style.eye = QRCode.FillStyle.Solid(foregroundColor)
document.design.style.eyeBackground = getColor(from: styleData.backgroundColor) ?? CGColor(red: 1, green: 1, blue: 1, alpha: 1)
//
let dotType = getDotType(from: styleData.dotType)
document.design.shape.onPixels = dotType
document.design.style.onPixels = QRCode.FillStyle.Solid(foregroundColor)
document.design.style.onPixelsBackground = getColor(from: styleData.backgroundColor) ?? CGColor(red: 1, green: 1, blue: 1, alpha: 1)
document.design.shape.offPixels = dotType
document.design.style.offPixels = QRCode.FillStyle.Solid(getColor(from: styleData.backgroundColor) ?? CGColor(red: 1, green: 1, blue: 1, alpha: 1))
document.design.style.offPixelsBackground = getColor(from: styleData.backgroundColor) ?? CGColor(red: 1, green: 1, blue: 1, alpha: 1)
//
let eyeType = getEyeType(from: styleData.eyeType)
document.design.shape.eye = eyeType
// Logo
if let logo = styleData.logo {
applyLogo(logo, hasCustomLogo: styleData.hasCustomLogo, customLogoFileName: styleData.customLogoFileName, to: document)
}
}
// MARK: - Logo
private static func applyLogo(
_ logoIdentifier: String,
hasCustomLogo: Bool,
customLogoFileName: String?,
to document: QRCode.Document
) {
if hasCustomLogo, let fileName = customLogoFileName {
// Logo
if let customLogoImage = loadCustomLogoImage(fileName: fileName),
let cgImage = customLogoImage.cgImage {
document.logoTemplate = QRCode.LogoTemplate.CircleCenter(image: cgImage, inset: 0)
}
} else {
// Logo
if let qrCodeLogo = QRCodeLogo(rawValue: logoIdentifier),
let logoImage = qrCodeLogo.image,
let cgImage = logoImage.cgImage {
document.logoTemplate = QRCode.LogoTemplate.CircleCenter(image: cgImage)
}
}
}
// MARK: - Logo
private static func loadCustomLogoImage(fileName: String) -> UIImage? {
guard let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let customLogosPath = documentsPath.appendingPathComponent("CustomLogos")
let imagePath = customLogosPath.appendingPathComponent(fileName)
return UIImage(contentsOfFile: imagePath.path)
}
// MARK: -
private static func getColor(from colorString: String) -> CGColor? {
switch colorString.lowercased() {
case "black": return CGColor(red: 0, green: 0, blue: 0, alpha: 1)
case "white": return CGColor(red: 1, green: 1, blue: 1, alpha: 1)
case "red": return CGColor(red: 1, green: 0, blue: 0, alpha: 1)
case "blue": return CGColor(red: 0, green: 0, blue: 1, alpha: 1)
case "green": return CGColor(red: 0, green: 1, blue: 0, alpha: 1)
case "yellow": return CGColor(red: 1, green: 1, blue: 0, alpha: 1)
case "purple": return CGColor(red: 0.5, green: 0, blue: 0.5, alpha: 1)
case "orange": return CGColor(red: 1, green: 0.5, blue: 0, alpha: 1)
case "pink": return CGColor(red: 1, green: 0.75, blue: 0.8, alpha: 1)
case "cyan": return CGColor(red: 0, green: 1, blue: 1, alpha: 1)
case "magenta": return CGColor(red: 1, green: 0, blue: 1, alpha: 1)
case "brown": return CGColor(red: 0.6, green: 0.4, blue: 0.2, alpha: 1)
case "gray": return CGColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1)
case "navy": return CGColor(red: 0, green: 0, blue: 0.5, alpha: 1)
case "teal": return CGColor(red: 0, green: 0.5, blue: 0.5, alpha: 1)
case "indigo": return CGColor(red: 0.3, green: 0, blue: 0.7, alpha: 1)
case "lime": return CGColor(red: 0.5, green: 1, blue: 0, alpha: 1)
case "maroon": return CGColor(red: 0.5, green: 0, blue: 0, alpha: 1)
case "olive": return CGColor(red: 0.5, green: 0.5, blue: 0, alpha: 1)
case "silver": return CGColor(red: 0.75, green: 0.75, blue: 0.75, alpha: 1)
default: return nil
}
}
// MARK: -
private static func getDotType(from dotTypeString: String) -> QRCodePixelShapeGenerator {
// QRCodeDotType
if let dotType = QRCodeDotType.allCases.first(where: { $0.rawValue == dotTypeString }) {
return dotType.pixelShape
}
//
return QRCode.PixelShape.Square()
}
// MARK: -
private static func getEyeType(from eyeTypeString: String) -> QRCodeEyeShapeGenerator {
// QRCodeEyeType
if let eyeType = QRCodeEyeType.allCases.first(where: { $0.rawValue == eyeTypeString }) {
return eyeType.eyeShape
}
//
return QRCode.EyeShape.Square()
}
}