@ -5,7 +5,7 @@ internal import SwiftImageReadWrite
struct QRCodeDetailView : View {
struct QRCodeDetailView : View {
let historyItem : HistoryItem
let historyItem : HistoryItem
@ StateObject private var coreDataManager = CoreDataManager . shared
@ EnvironmentObject var coreDataManager : CoreDataManager
@ State private var qrCodeImage : UIImage ?
@ State private var qrCodeImage : UIImage ?
@ State private var showingShareSheet = false
@ State private var showingShareSheet = false
@ State private var showingAlert = false
@ State private var showingAlert = false
@ -15,8 +15,8 @@ struct QRCodeDetailView: View {
ScrollView {
ScrollView {
VStack ( spacing : 20 ) {
VStack ( spacing : 20 ) {
// 二 维 码 图 片
// 二 维 码 图 片
qrCode ImageView
qrCode StyleSection
// 二 维 码 类 型 信 息
// 二 维 码 类 型 信 息
qrCodeTypeSection
qrCodeTypeSection
@ -201,6 +201,68 @@ struct QRCodeDetailView: View {
. shadow ( radius : 2 )
. shadow ( radius : 2 )
}
}
// MARK: - 二 维 码 样 式 信 息
private var qrCodeStyleSection : some View {
VStack ( spacing : 0 ) {
if let styleData = getStyleData ( ) {
// 使 用 样 式 生 成 二 维 码 预 览
VStack ( spacing : 16 ) {
// 样 式 预 览 二 维 码
if let previewImage = generateStylePreviewImage ( styleData : styleData ) {
Image ( uiImage : previewImage )
. resizable ( )
. aspectRatio ( contentMode : . fit )
. frame ( maxWidth : 200 , maxHeight : 200 )
. padding ( )
. background ( Color . white )
. cornerRadius ( 12 )
. shadow ( radius : 4 )
}
// 样 式 标 签
HStack ( spacing : 8 ) {
Label ( " 自定义样式 " , systemImage : " paintpalette " )
. font ( . caption )
. padding ( . horizontal , 8 )
. padding ( . vertical , 4 )
. background ( Color . purple . opacity ( 0.2 ) )
. foregroundColor ( . purple )
. cornerRadius ( 6 )
}
}
. padding ( )
. background ( Color . purple . opacity ( 0.05 ) )
. cornerRadius ( 12 )
} else {
// 标 准 样 式 预 览
VStack ( spacing : 16 ) {
if let standardImage = generateStandardQRCodeImage ( ) {
Image ( uiImage : standardImage )
. resizable ( )
. aspectRatio ( contentMode : . fit )
. frame ( maxWidth : 200 , maxHeight : 200 )
. padding ( )
. background ( Color . white )
. cornerRadius ( 12 )
. shadow ( radius : 4 )
}
Label ( " 标准样式 " , systemImage : " qrcode " )
. font ( . caption )
. padding ( . horizontal , 8 )
. padding ( . vertical , 4 )
. background ( Color . gray . opacity ( 0.2 ) )
. foregroundColor ( . gray )
. cornerRadius ( 6 )
}
. padding ( )
. background ( Color . gray . opacity ( 0.05 ) )
. cornerRadius ( 12 )
}
}
. padding ( )
}
// MARK: - 操 作 按 钮
// MARK: - 操 作 按 钮
private var actionButtonsSection : some View {
private var actionButtonsSection : some View {
VStack ( spacing : 12 ) {
VStack ( spacing : 12 ) {
@ -329,55 +391,55 @@ struct ShareSheet: UIViewControllerRepresentable {
# Preview ( " Wi‑ Fi " ) {
# Preview ( " Wi‑ Fi " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . wifiSample ( in : ctx )
let item = PreviewData . wifiSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " URL " ) {
# Preview ( " URL " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . urlSample ( in : ctx )
let item = PreviewData . urlSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " SMS " ) {
# Preview ( " SMS " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . smsSample ( in : ctx )
let item = PreviewData . smsSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " vCard " ) {
# Preview ( " vCard " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . vcardSample ( in : ctx )
let item = PreviewData . vcardSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " Instagram " ) {
# Preview ( " Instagram " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . instagramSample ( in : ctx )
let item = PreviewData . instagramSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " WhatsApp " ) {
# Preview ( " WhatsApp " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . whatsappSample ( in : ctx )
let item = PreviewData . whatsappSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " Viber " ) {
# Preview ( " Viber " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . viberSample ( in : ctx )
let item = PreviewData . viberSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " Text " ) {
# Preview ( " Text " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . textSample ( in : ctx )
let item = PreviewData . textSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
# Preview ( " MeCard " ) {
# Preview ( " MeCard " ) {
let ctx = PreviewData . context
let ctx = PreviewData . context
let item = PreviewData . mecardSample ( in : ctx )
let item = PreviewData . mecardSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
NavigationView { QRCodeDetailView ( historyItem : item ) }
}
}
// MARK: - P r e v i e w D a t a
// MARK: - P r e v i e w D a t a
@ -449,14 +511,197 @@ private enum PreviewData {
let content = " Hello, this is a text message! "
let content = " Hello, this is a text message! "
return makeBaseItem ( in : context , content : content , qrType : . text )
return makeBaseItem ( in : context , content : content , qrType : . text )
}
}
static func mecardSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " MECARD:N:Doe,John;NICKNAME:Johnny;TEL:+1234567890;EMAIL:john.doe@example.com;ORG:Example Company;ADR:123 Main St,Anytown,CA,12345,USA;URL:https://example.com;BDAY:19820908;NOTE:Software Engineer;; "
return makeBaseItem ( in : context , content : content , qrType : . mecard )
}
static func viberSample ( in context : NSManagedObjectContext ) -> HistoryItem {
static func viberSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " viber://add?number=+1234567890 "
let content = " viber://add?number=+1234567890 "
return makeBaseItem ( in : context , content : content , qrType : . viber )
return makeBaseItem ( in : context , content : content , qrType : . viber )
}
}
static func mecardSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " MECARD:N:Doe,John;NICKNAME:Johnny;TEL:+1234567890;EMAIL:john.doe@example.com;ORG:Example Company;TITLE:Software Engineer;ADR:123 Main St,Anytown,CA,12345,USA;URL:https://example.com;NOTE:This is a note; "
return makeBaseItem ( in : context , content : content , qrType : . mecard )
}
}
// MARK: - 样 式 信 息 辅 助 方 法
extension QRCodeDetailView {
// MARK: - 生 成 样 式 预 览 图 片
private func generateStylePreviewImage ( styleData : QRCodeStyleData ) -> UIImage ? {
guard let content = historyItem . content else { return nil }
do {
var qrCodeBuilder = try QRCode . build
. text ( content )
. quietZonePixelCount ( 0 )
// 设 置 前 景 色
if let foregroundColor = getColorFromString ( styleData . foregroundColor ) {
qrCodeBuilder = qrCodeBuilder . foregroundColor ( foregroundColor )
}
// 设 置 背 景 色
if let backgroundColor = getColorFromString ( styleData . backgroundColor ) {
qrCodeBuilder = qrCodeBuilder . backgroundColor ( backgroundColor )
}
// 设 置 背 景 圆 角
qrCodeBuilder = qrCodeBuilder . background . cornerRadius ( 3 )
// 设 置 点 类 型
if let dotType = getDotTypeFromString ( styleData . dotType ) {
qrCodeBuilder = qrCodeBuilder . onPixels . shape ( dotType )
}
// 设 置 眼 睛 类 型
if let eyeType = getEyeTypeFromString ( styleData . eyeType ) {
qrCodeBuilder = qrCodeBuilder . eye . shape ( eyeType )
}
// 设 置 L o g o ( 如 果 有 的 话 )
if let logo = styleData . logo , ! logo . isEmpty {
if let logoTemplate = getLogoFromString ( logo ) {
qrCodeBuilder = qrCodeBuilder . logo ( logoTemplate )
}
}
// 生 成 图 片
let imageData = try qrCodeBuilder . generate . image ( dimension : 300 , representation : . png ( ) )
return UIImage ( data : imageData )
} catch {
print ( " 生成样式预览图片失败: \( error ) " )
return nil
}
}
// MARK: - 生 成 标 准 二 维 码 图 片
private func generateStandardQRCodeImage ( ) -> UIImage ? {
guard let content = historyItem . content else { return nil }
do {
let imageData = try QRCode . build
. text ( content )
. quietZonePixelCount ( 0 )
. foregroundColor ( CGColor ( srgbRed : 0 , green : 0 , blue : 0 , alpha : 1 ) )
. backgroundColor ( CGColor ( srgbRed : 1 , green : 1 , blue : 1 , alpha : 1 ) )
. generate . image ( dimension : 300 , representation : . png ( ) )
return UIImage ( data : imageData )
} catch {
print ( " 生成标准二维码图片失败: \( error ) " )
return nil
}
}
// MARK: - 颜 色 转 换
private func getColorFromString ( _ colorString : String ) -> CGColor ? {
if let color = QRCodeColor ( rawValue : colorString ) {
return color . cgColor
}
return nil
}
// MARK: - 点 类 型 转 换
private func getDotTypeFromString ( _ dotTypeString : String ) -> QRCodePixelShapeGenerator ? {
if let dotType = QRCodeDotType ( rawValue : dotTypeString ) {
return dotType . pixelShape
}
return nil
}
// MARK: - 眼 睛 类 型 转 换
private func getEyeTypeFromString ( _ eyeTypeString : String ) -> QRCodeEyeShapeGenerator ? {
if let eyeType = QRCodeEyeType ( rawValue : eyeTypeString ) {
return eyeType . eyeShape
}
return nil
}
// MARK: - L o g o 转 换
private func getLogoFromString ( _ logoString : String ) -> QRCode . LogoTemplate ? {
// 检 查 是 否 是 自 定 义 L o g o
if logoString . hasPrefix ( " custom_ " ) {
// 从 样 式 数 据 中 获 取 自 定 义 L o g o 图 片
if let styleData = getStyleData ( ) ,
let customImage = styleData . customLogoImage ,
let cgImage = customImage . cgImage {
return QRCode . LogoTemplate . CircleCenter ( image : cgImage , inset : 0 )
}
} else {
// 预 设 L o g o
if let logo = QRCodeLogo ( rawValue : logoString ) ,
let image = logo . image ,
let cgImage = image . cgImage {
return QRCode . LogoTemplate . CircleCenter ( image : cgImage )
}
}
return nil
}
// MARK: - 从 J S O N 字 符 串 解 析 样 式 数 据
private func getStyleData ( ) -> QRCodeStyleData ? {
guard let jsonString = historyItem . 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: - 显 示 名 称 转 换 方 法 ( 保 留 原 有 方 法 )
private func getColorDisplayName ( _ colorString : String ) -> String {
if let color = QRCodeColor ( rawValue : colorString ) {
switch color {
case . black : return " 黑色 "
case . white : return " 白色 "
case . red : return " 红色 "
case . blue : return " 蓝色 "
case . green : return " 绿色 "
case . yellow : return " 黄色 "
case . purple : return " 紫色 "
case . orange : return " 橙色 "
case . pink : return " 粉色 "
case . cyan : return " 青色 "
case . magenta : return " 洋红色 "
case . brown : return " 棕色 "
case . gray : return " 灰色 "
case . navy : return " 海军蓝 "
case . teal : return " 蓝绿色 "
case . indigo : return " 靛蓝色 "
case . lime : return " 青柠色 "
case . maroon : return " 栗色 "
case . olive : return " 橄榄色 "
case . silver : return " 银色 "
}
}
return colorString
}
private func getDotTypeDisplayName ( _ dotTypeString : String ) -> String {
if let dotType = QRCodeDotType ( rawValue : dotTypeString ) {
return dotType . displayName
}
return dotTypeString
}
private func getEyeTypeDisplayName ( _ eyeTypeString : String ) -> String {
if let eyeType = QRCodeEyeType ( rawValue : eyeTypeString ) {
return eyeType . displayName
}
return eyeTypeString
}
private func getLogoDisplayName ( _ logoString : String ) -> String {
if let logo = QRCodeLogo ( rawValue : logoString ) {
return logo . displayName
}
return logoString
}
}
}