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

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()
}
}