import SwiftUI
import CoreData
import QRCode
internal import SwiftImageReadWrite
struct QRCodeDetailView : View {
let historyItem : HistoryItem
@ StateObject private var coreDataManager = CoreDataManager . shared
@ State private var qrCodeImage : UIImage ?
@ State private var showingShareSheet = false
@ State private var showingAlert = false
@ State private var alertMessage = " "
var body : some View {
ScrollView {
VStack ( spacing : 20 ) {
// 二 维 码 图 片
qrCodeImageView
// 二 维 码 类 型 信 息
qrCodeTypeSection
// 解 析 后 的 详 细 信 息
parsedInfoSection
// 原 始 内 容
originalContentSection
// 操 作 按 钮
actionButtonsSection
}
. padding ( )
}
. navigationTitle ( " 二维码详情 " )
. navigationBarTitleDisplayMode ( . inline )
. toolbar {
ToolbarItem ( placement : . navigationBarTrailing ) {
Button ( action : {
showingShareSheet = true
} ) {
Image ( systemName : " square.and.arrow.up " )
}
}
}
. onAppear {
generateQRCodeImage ( )
}
. sheet ( isPresented : $ showingShareSheet ) {
ShareSheet ( activityItems : [ historyItem . content ? ? " " ] )
}
. alert ( " 提示 " , isPresented : $ showingAlert ) {
Button ( " 确定 " ) { }
} message : {
Text ( alertMessage )
}
}
// MARK: - 二 维 码 图 片 视 图
private var qrCodeImageView : some View {
VStack ( spacing : 16 ) {
if let qrCodeImage = qrCodeImage {
Image ( uiImage : qrCodeImage )
. resizable ( )
. aspectRatio ( contentMode : . fit )
. frame ( width : 200 , height : 200 )
. cornerRadius ( 12 )
. shadow ( radius : 8 )
} else {
RoundedRectangle ( cornerRadius : 12 )
. fill ( Color . gray . opacity ( 0.3 ) )
. frame ( width : 200 , height : 200 )
. overlay (
ProgressView ( )
. scaleEffect ( 1.5 )
)
}
Text ( " 扫描此二维码 " )
. font ( . caption )
. foregroundColor ( . secondary )
}
}
// MARK: - 二 维 码 类 型 信 息
private var qrCodeTypeSection : some View {
VStack ( alignment : . leading , spacing : 12 ) {
HStack {
Image ( systemName : " qrcode " )
. font ( . title2 )
. foregroundColor ( . blue )
Text ( " 二维码类型 " )
. font ( . headline )
Spacer ( )
}
if let qrCodeTypeString = historyItem . qrCodeType ,
let qrCodeType = QRCodeType ( rawValue : qrCodeTypeString ) {
HStack {
Image ( systemName : qrCodeType . icon )
. font ( . title3 )
. foregroundColor ( . orange )
Text ( qrCodeType . displayName )
. font ( . title3 )
. fontWeight ( . medium )
Spacer ( )
}
. padding ( )
. background ( Color . orange . opacity ( 0.1 ) )
. cornerRadius ( 8 )
}
}
. padding ( )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 12 )
. shadow ( radius : 2 )
}
// MARK: - 解 析 后 的 详 细 信 息
private var parsedInfoSection : some View {
VStack ( alignment : . leading , spacing : 12 ) {
HStack {
Image ( systemName : " info.circle " )
. font ( . title2 )
. foregroundColor ( . green )
Text ( " 解析信息 " )
. font ( . headline )
Spacer ( )
}
if let content = historyItem . content {
let parsedData = QRCodeParser . parseQRCode ( content )
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Image ( systemName : parsedData . icon )
. font ( . title3 )
. foregroundColor ( . green )
Text ( parsedData . title )
. font ( . title3 )
. fontWeight ( . medium )
Spacer ( )
}
if let subtitle = parsedData . subtitle {
Text ( subtitle )
. font ( . body )
. foregroundColor ( . secondary )
. multilineTextAlignment ( . leading )
}
}
. padding ( )
. background ( Color . green . opacity ( 0.1 ) )
. cornerRadius ( 8 )
}
}
. padding ( )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 12 )
. shadow ( radius : 2 )
}
// MARK: - 原 始 内 容
private var originalContentSection : some View {
VStack ( alignment : . leading , spacing : 12 ) {
HStack {
Image ( systemName : " doc.text " )
. font ( . title2 )
. foregroundColor ( . purple )
Text ( " 原始内容 " )
. font ( . headline )
Spacer ( )
}
if let content = historyItem . content {
ScrollView {
Text ( content )
. font ( . system ( . body , design : . monospaced ) )
. foregroundColor ( . secondary )
. multilineTextAlignment ( . leading )
. padding ( )
. frame ( maxWidth : . infinity , alignment : . leading )
}
. frame ( maxHeight : 200 )
. background ( Color . purple . opacity ( 0.1 ) )
. cornerRadius ( 8 )
}
}
. padding ( )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 12 )
. shadow ( radius : 2 )
}
// MARK: - 操 作 按 钮
private var actionButtonsSection : some View {
VStack ( spacing : 12 ) {
// 收 藏 按 钮
Button ( action : toggleFavorite ) {
HStack {
Image ( systemName : historyItem . isFavorite ? " heart.fill " : " heart " )
. foregroundColor ( historyItem . isFavorite ? . red : . gray )
Text ( historyItem . isFavorite ? " 取消收藏 " : " 收藏 " )
. fontWeight ( . medium )
}
. frame ( maxWidth : . infinity )
. padding ( )
. background ( historyItem . isFavorite ? Color . red . opacity ( 0.1 ) : Color . gray . opacity ( 0.1 ) )
. foregroundColor ( historyItem . isFavorite ? . red : . gray )
. cornerRadius ( 10 )
}
// 复 制 内 容 按 钮
Button ( action : copyContent ) {
HStack {
Image ( systemName : " doc.on.doc " )
. foregroundColor ( . blue )
Text ( " 复制内容 " )
. fontWeight ( . medium )
}
. frame ( maxWidth : . infinity )
. padding ( )
. background ( Color . blue . opacity ( 0.1 ) )
. foregroundColor ( . blue )
. cornerRadius ( 10 )
}
// 打 开 链 接 按 钮 ( 如 果 是 U R L 类 型 )
if let content = historyItem . content , canOpenURL ( content ) {
Button ( action : { openURL ( content ) } ) {
HStack {
Image ( systemName : " arrow.up.right.square " )
. foregroundColor ( . green )
Text ( " 打开链接 " )
. fontWeight ( . medium )
}
. frame ( maxWidth : . infinity )
. padding ( )
. background ( Color . green . opacity ( 0.1 ) )
. foregroundColor ( . green )
. cornerRadius ( 10 )
}
}
}
. padding ( )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 12 )
. shadow ( radius : 2 )
}
// MARK: - 生 成 二 维 码 图 片
private func generateQRCodeImage ( ) {
guard let content = historyItem . content else { return }
do {
let imageData = try QRCode . build
. text ( content )
. quietZonePixelCount ( 3 )
. foregroundColor ( CGColor ( srgbRed : 1 , green : 0 , blue : 0.6 , alpha : 1 ) )
. backgroundColor ( CGColor ( srgbRed : 0 , green : 0 , blue : 0.2 , alpha : 1 ) )
. background . cornerRadius ( 3 )
. onPixels . shape ( QRCode . PixelShape . CurvePixel ( ) )
. eye . shape ( QRCode . EyeShape . Teardrop ( ) )
. generate . image ( dimension : 600 , representation : . png ( ) )
self . qrCodeImage = UIImage ( data : imageData )
} catch {
print ( " 生成二维码失败: \( error ) " )
}
}
// MARK: - 切 换 收 藏 状 态
private func toggleFavorite ( ) {
historyItem . isFavorite . toggle ( )
coreDataManager . save ( )
let message = historyItem . isFavorite ? " 已添加到收藏 " : " 已取消收藏 "
alertMessage = message
showingAlert = true
}
// MARK: - 复 制 内 容
private func copyContent ( ) {
if let content = historyItem . content {
UIPasteboard . general . string = content
alertMessage = " 内容已复制到剪贴板 "
showingAlert = true
}
}
// MARK: - 检 查 是 否 可 以 打 开 U R L
private func canOpenURL ( _ string : String ) -> Bool {
guard let url = URL ( string : string ) else { return false }
return UIApplication . shared . canOpenURL ( url )
}
// MARK: - 打 开 U R L
private func openURL ( _ string : String ) {
guard let url = URL ( string : string ) else { return }
UIApplication . shared . open ( url )
}
}
// MARK: - 分 享 表 单
struct ShareSheet : UIViewControllerRepresentable {
let activityItems : [ Any ]
func makeUIViewController ( context : Context ) -> UIActivityViewController {
let controller = UIActivityViewController ( activityItems : activityItems , applicationActivities : nil )
return controller
}
func updateUIViewController ( _ uiViewController : UIActivityViewController , context : Context ) { }
}
# Preview ( " Wi‑ Fi " ) {
let ctx = PreviewData . context
let item = PreviewData . wifiSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " URL " ) {
let ctx = PreviewData . context
let item = PreviewData . urlSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " SMS " ) {
let ctx = PreviewData . context
let item = PreviewData . smsSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " vCard " ) {
let ctx = PreviewData . context
let item = PreviewData . vcardSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " Instagram " ) {
let ctx = PreviewData . context
let item = PreviewData . instagramSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " WhatsApp " ) {
let ctx = PreviewData . context
let item = PreviewData . whatsappSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " Text " ) {
let ctx = PreviewData . context
let item = PreviewData . textSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
# Preview ( " MeCard " ) {
let ctx = PreviewData . context
let item = PreviewData . mecardSample ( in : ctx )
return NavigationView { QRCodeDetailView ( historyItem : item ) }
}
// MARK: - P r e v i e w D a t a
private enum PreviewData {
static let context : NSManagedObjectContext = {
let container = NSPersistentContainer ( name : " MyQrCode " )
let description = NSPersistentStoreDescription ( )
description . type = NSInMemoryStoreType
container . persistentStoreDescriptions = [ description ]
container . loadPersistentStores { _ , _ in }
return container . viewContext
} ( )
private static func makeBaseItem ( in context : NSManagedObjectContext , content : String , qrType : QRCodeType , favorite : Bool = false ) -> HistoryItem {
let item = HistoryItem ( context : context )
item . id = UUID ( )
item . content = content
item . dataType = DataType . qrcode . rawValue
item . dataSource = DataSource . created . rawValue
item . createdAt = Date ( )
item . isFavorite = favorite
item . qrCodeType = qrType . rawValue
return item
}
static func wifiSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " WIFI:T:WPA;S:MyNetwork;P:MyPassword;; "
return makeBaseItem ( in : context , content : content , qrType : . wifi , favorite : true )
}
static func urlSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " https://www.example.com "
return makeBaseItem ( in : context , content : content , qrType : . url )
}
static func smsSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " SMSTO:+8613800138000:Hello "
return makeBaseItem ( in : context , content : content , qrType : . sms )
}
static func vcardSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " " "
BEGIN : VCARD
VERSION : 3.0
N : Doe ; John ; ; ;
FN : John Doe
TEL ; TYPE = WORK , CELL : ( 123 ) 456 - 7890
EMAIL ; TYPE = PREF , INTERNET : john . doe @ example . com
ORG : Example Company
TITLE : Software Engineer
ADR ; TYPE = WORK : ; ; 123 Main St ; Anytown ; CA ; 12345 ; USA
URL : https : // e x a m p l e . c o m
END : VCARD
" " " .trimmingCharacters(in: .whitespacesAndNewlines)
return makeBaseItem ( in : context , content : content , qrType : . vcard )
}
static func instagramSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " instagram://user?username=example_user "
return makeBaseItem ( in : context , content : content , qrType : . instagram )
}
static func whatsappSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " whatsapp://send?phone=+1234567890 "
return makeBaseItem ( in : context , content : content , qrType : . whatsapp )
}
static func textSample ( in context : NSManagedObjectContext ) -> HistoryItem {
let content = " Hello, this is a text message! "
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 )
}
}