From 06fe117f68926a97811981642ecd03795aa4ebbf Mon Sep 17 00:00:00 2001 From: v504 Date: Fri, 29 Aug 2025 18:24:38 +0800 Subject: [PATCH] Enhance HistoryView navigation by differentiating QR code item handling based on data source. Implement new methods for generating QR code images and extracting style data, improving user experience and functionality in HistoryItemRow. --- MyQrCode/Utils/QRCodeGenerator.swift | 191 +++++++++++++++++++++++ MyQrCode/Views/History/HistoryView.swift | 64 +++++++- 2 files changed, 250 insertions(+), 5 deletions(-) create mode 100644 MyQrCode/Utils/QRCodeGenerator.swift 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: - 清空历史记录确认视图