diff --git a/MyQrCode/Utils/QRCodeGenerator.swift b/MyQrCode/Utils/QRCodeGenerator.swift new file mode 100644 index 0000000..4024594 --- /dev/null +++ b/MyQrCode/Utils/QRCodeGenerator.swift @@ -0,0 +1,191 @@ +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() + } +} diff --git a/MyQrCode/Views/History/HistoryView.swift b/MyQrCode/Views/History/HistoryView.swift index b1e67da..d0ff4b1 100644 --- a/MyQrCode/Views/History/HistoryView.swift +++ b/MyQrCode/Views/History/HistoryView.swift @@ -647,13 +647,28 @@ struct HistoryItemRow: View { } } .background( - // 根据数据类型添加导航链接 + // 根据数据类型和数据源添加导航链接 Group { if item.dataType == DataType.qrcode.rawValue { - NavigationLink( - destination: QRCodeDetailView(historyItem: item), - label: { EmptyView() } - ) + if item.dataSource == DataSource.created.rawValue { + // 创建类型的二维码条目,导航到保存界面 + NavigationLink( + destination: QRCodeSavedView( + qrCodeImage: generateQRCodeImage(from: item), + qrCodeContent: item.content ?? "", + qrCodeType: getQRCodeType(from: item), + styleData: getStyleData(from: item), + historyItem: item + ), + label: { EmptyView() } + ) + } else { + // 扫描类型的二维码条目,导航到详情界面 + NavigationLink( + destination: QRCodeDetailView(historyItem: item), + label: { EmptyView() } + ) + } } else if item.dataType == DataType.barcode.rawValue { NavigationLink( destination: BarcodeDetailView(historyItem: item), @@ -670,6 +685,45 @@ struct HistoryItemRow: View { formatter.timeStyle = .short return formatter.string(from: date) } + + // MARK: - QRCodeSavedView 辅助方法 + private func generateQRCodeImage(from item: HistoryItem) -> UIImage { + // 从历史记录项生成二维码图片 + let content = item.content ?? "" + + // 获取样式数据 + let styleData = getStyleData(from: item) + + // 使用高质量二维码生成器 + return QRCodeGenerator.generateHighQualityQRCode( + content: content, + styleData: styleData + ) + } + + private func getQRCodeType(from item: HistoryItem) -> QRCodeType { + if let qrCodeTypeString = item.qrCodeType, + let qrCodeType = QRCodeType(rawValue: qrCodeTypeString) { + return qrCodeType + } + return .text // 默认类型 + } + + private func getStyleData(from item: HistoryItem) -> QRCodeStyleData? { + // 从历史记录项中提取样式数据 + guard let jsonString = item.qrCodeStyleData, + let jsonData = jsonString.data(using: .utf8) else { + return nil + } + + do { + let styleData = try JSONDecoder().decode(QRCodeStyleData.self, from: jsonData) + return styleData + } catch { + print("❌ 样式数据JSON解码失败:\(error)") + return nil + } + } } // MARK: - 清空历史记录确认视图