Add QR code detail navigation to HistoryView; implement overlay button for viewing details and enable tap gesture for item details in HistoryItemRow, enhancing user interaction and experience.
parent
dc8006d1bb
commit
f408fa0c04
@ -0,0 +1,400 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
// MARK: - 二维码解析器
|
||||
class QRCodeParser {
|
||||
|
||||
// MARK: - 解析二维码内容
|
||||
static func parseQRCode(_ content: String) -> ParsedQRData {
|
||||
let trimmedContent = content.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Wi-Fi
|
||||
if trimmedContent.hasPrefix("WIFI:") {
|
||||
return parseWiFi(trimmedContent)
|
||||
}
|
||||
|
||||
// Email
|
||||
if trimmedContent.hasPrefix("mailto:") {
|
||||
return parseEmail(trimmedContent)
|
||||
}
|
||||
|
||||
// Phone
|
||||
if trimmedContent.hasPrefix("tel:") {
|
||||
return parsePhone(trimmedContent)
|
||||
}
|
||||
|
||||
// SMS
|
||||
if trimmedContent.hasPrefix("sms:") {
|
||||
return parseSMS(trimmedContent)
|
||||
}
|
||||
|
||||
// vCard
|
||||
if trimmedContent.hasPrefix("BEGIN:VCARD") {
|
||||
return parseVCard(trimmedContent)
|
||||
}
|
||||
|
||||
// MeCard
|
||||
if trimmedContent.hasPrefix("MECARD:") {
|
||||
return parseMeCard(trimmedContent)
|
||||
}
|
||||
|
||||
// Calendar
|
||||
if trimmedContent.hasPrefix("BEGIN:VEVENT") {
|
||||
return parseCalendar(trimmedContent)
|
||||
}
|
||||
|
||||
// Instagram
|
||||
if trimmedContent.contains("instagram.com") {
|
||||
return parseInstagram(trimmedContent)
|
||||
}
|
||||
|
||||
// Facebook
|
||||
if trimmedContent.contains("facebook.com") {
|
||||
return parseFacebook(trimmedContent)
|
||||
}
|
||||
|
||||
// Spotify
|
||||
if trimmedContent.hasPrefix("spotify:") {
|
||||
return parseSpotify(trimmedContent)
|
||||
}
|
||||
|
||||
// Twitter
|
||||
if trimmedContent.contains("twitter.com") {
|
||||
return parseTwitter(trimmedContent)
|
||||
}
|
||||
|
||||
// WhatsApp
|
||||
if trimmedContent.contains("wa.me") {
|
||||
return parseWhatsApp(trimmedContent)
|
||||
}
|
||||
|
||||
// Viber
|
||||
if trimmedContent.hasPrefix("viber://") {
|
||||
return parseViber(trimmedContent)
|
||||
}
|
||||
|
||||
// Snapchat
|
||||
if trimmedContent.hasPrefix("snapchat://") {
|
||||
return parseSnapchat(trimmedContent)
|
||||
}
|
||||
|
||||
// TikTok
|
||||
if trimmedContent.contains("tiktok.com") {
|
||||
return parseTikTok(trimmedContent)
|
||||
}
|
||||
|
||||
// URL (检查是否为有效的URL)
|
||||
if isValidURL(trimmedContent) {
|
||||
return parseURL(trimmedContent)
|
||||
}
|
||||
|
||||
// Location
|
||||
if trimmedContent.hasPrefix("geo:") {
|
||||
return parseLocation(trimmedContent)
|
||||
}
|
||||
|
||||
// 默认为文本类型
|
||||
return ParsedQRData(
|
||||
type: .text,
|
||||
title: "文本信息",
|
||||
subtitle: trimmedContent.count > 50 ? String(trimmedContent.prefix(50)) + "..." : trimmedContent,
|
||||
icon: "text.quote"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Wi-Fi
|
||||
private static func parseWiFi(_ content: String) -> ParsedQRData {
|
||||
let wifiInfo = content.replacingOccurrences(of: "WIFI:", with: "")
|
||||
let components = wifiInfo.components(separatedBy: ";")
|
||||
|
||||
var ssid = ""
|
||||
var password = ""
|
||||
var encryption = "WPA"
|
||||
|
||||
for component in components {
|
||||
if component.hasPrefix("S:") {
|
||||
ssid = String(component.dropFirst(2))
|
||||
} else if component.hasPrefix("P:") {
|
||||
password = String(component.dropFirst(2))
|
||||
} else if component.hasPrefix("T:") {
|
||||
encryption = String(component.dropFirst(2))
|
||||
}
|
||||
}
|
||||
|
||||
let title = "Wi-Fi网络"
|
||||
let subtitle = "网络名称: \(ssid)\n加密类型: \(encryption)\n密码: \(password.isEmpty ? "无" : "已设置")"
|
||||
|
||||
return ParsedQRData(
|
||||
type: .wifi,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: "wifi"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Email
|
||||
private static func parseEmail(_ content: String) -> ParsedQRData {
|
||||
let email = content.replacingOccurrences(of: "mailto:", with: "")
|
||||
|
||||
return ParsedQRData(
|
||||
type: .mail,
|
||||
title: "邮箱地址",
|
||||
subtitle: email,
|
||||
icon: "envelope"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Phone
|
||||
private static func parsePhone(_ content: String) -> ParsedQRData {
|
||||
let phone = content.replacingOccurrences(of: "tel:", with: "")
|
||||
|
||||
return ParsedQRData(
|
||||
type: .phone,
|
||||
title: "电话号码",
|
||||
subtitle: phone,
|
||||
icon: "phone"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析SMS
|
||||
private static func parseSMS(_ content: String) -> ParsedQRData {
|
||||
let smsInfo = content.replacingOccurrences(of: "sms:", with: "")
|
||||
let components = smsInfo.components(separatedBy: "?body=")
|
||||
|
||||
let phone = components.first ?? ""
|
||||
let message = components.count > 1 ? components[1] : ""
|
||||
|
||||
let title = "短信"
|
||||
let subtitle = "号码: \(phone)\n内容: \(message)"
|
||||
|
||||
return ParsedQRData(
|
||||
type: .sms,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: "message"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析vCard
|
||||
private static func parseVCard(_ content: String) -> ParsedQRData {
|
||||
let lines = content.components(separatedBy: .newlines)
|
||||
var name = ""
|
||||
var phone = ""
|
||||
var email = ""
|
||||
|
||||
for line in lines {
|
||||
if line.hasPrefix("FN:") {
|
||||
name = String(line.dropFirst(3))
|
||||
} else if line.hasPrefix("TEL:") {
|
||||
phone = String(line.dropFirst(4))
|
||||
} else if line.hasPrefix("EMAIL:") {
|
||||
email = String(line.dropFirst(6))
|
||||
}
|
||||
}
|
||||
|
||||
let title = "联系人信息"
|
||||
let subtitle = "姓名: \(name)\n电话: \(phone)\n邮箱: \(email)"
|
||||
|
||||
return ParsedQRData(
|
||||
type: .vcard,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: "person.crop.rectangle"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析MeCard
|
||||
private static func parseMeCard(_ content: String) -> ParsedQRData {
|
||||
let mecardInfo = content.replacingOccurrences(of: "MECARD:", with: "")
|
||||
let components = mecardInfo.components(separatedBy: ";")
|
||||
|
||||
var name = ""
|
||||
var phone = ""
|
||||
var email = ""
|
||||
var address = ""
|
||||
|
||||
for component in components {
|
||||
if component.hasPrefix("N:") {
|
||||
name = String(component.dropFirst(2))
|
||||
} else if component.hasPrefix("TEL:") {
|
||||
phone = String(component.dropFirst(4))
|
||||
} else if component.hasPrefix("EMAIL:") {
|
||||
email = String(component.dropFirst(6))
|
||||
} else if component.hasPrefix("ADR:") {
|
||||
address = String(component.dropFirst(4))
|
||||
}
|
||||
}
|
||||
|
||||
let title = "联系人信息"
|
||||
let subtitle = "姓名: \(name)\n电话: \(phone)\n邮箱: \(email)\n地址: \(address)"
|
||||
|
||||
return ParsedQRData(
|
||||
type: .mecard,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: "person.crop.rectangle"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Calendar
|
||||
private static func parseCalendar(_ content: String) -> ParsedQRData {
|
||||
let lines = content.components(separatedBy: .newlines)
|
||||
var summary = ""
|
||||
var startTime = ""
|
||||
var endTime = ""
|
||||
var location = ""
|
||||
|
||||
for line in lines {
|
||||
if line.hasPrefix("SUMMARY:") {
|
||||
summary = String(line.dropFirst(8))
|
||||
} else if line.hasPrefix("DTSTART:") {
|
||||
startTime = String(line.dropFirst(8))
|
||||
} else if line.hasPrefix("DTEND:") {
|
||||
endTime = String(line.dropFirst(6))
|
||||
} else if line.hasPrefix("LOCATION:") {
|
||||
location = String(line.dropFirst(9))
|
||||
}
|
||||
}
|
||||
|
||||
let title = "日历事件"
|
||||
let subtitle = "事件: \(summary)\n开始: \(startTime)\n结束: \(endTime)\n地点: \(location)"
|
||||
|
||||
return ParsedQRData(
|
||||
type: .calendar,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: "calendar"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Instagram
|
||||
private static func parseInstagram(_ content: String) -> ParsedQRData {
|
||||
let username = content.components(separatedBy: "/").dropLast().last ?? ""
|
||||
|
||||
return ParsedQRData(
|
||||
type: .instagram,
|
||||
title: "Instagram",
|
||||
subtitle: "用户名: \(username)",
|
||||
icon: "camera"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Facebook
|
||||
private static func parseFacebook(_ content: String) -> ParsedQRData {
|
||||
let pageId = content.components(separatedBy: "/").dropLast().last ?? ""
|
||||
|
||||
return ParsedQRData(
|
||||
type: .facebook,
|
||||
title: "Facebook",
|
||||
subtitle: "页面: \(pageId)",
|
||||
icon: "person.2"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Spotify
|
||||
private static func parseSpotify(_ content: String) -> ParsedQRData {
|
||||
let trackId = content.replacingOccurrences(of: "spotify:track:", with: "")
|
||||
|
||||
return ParsedQRData(
|
||||
type: .spotify,
|
||||
title: "Spotify",
|
||||
subtitle: "曲目ID: \(trackId)",
|
||||
icon: "music.note"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Twitter
|
||||
private static func parseTwitter(_ content: String) -> ParsedQRData {
|
||||
let username = content.components(separatedBy: "/").dropLast().last ?? ""
|
||||
|
||||
return ParsedQRData(
|
||||
type: .twitter,
|
||||
title: "Twitter",
|
||||
subtitle: "用户名: \(username)",
|
||||
icon: "bird"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析WhatsApp
|
||||
private static func parseWhatsApp(_ content: String) -> ParsedQRData {
|
||||
let phone = content.replacingOccurrences(of: "https://wa.me/", with: "")
|
||||
|
||||
return ParsedQRData(
|
||||
type: .whatsapp,
|
||||
title: "WhatsApp",
|
||||
subtitle: "电话号码: \(phone)",
|
||||
icon: "message.circle"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Viber
|
||||
private static func parseViber(_ content: String) -> ParsedQRData {
|
||||
let phone = content.replacingOccurrences(of: "viber://contact?number=", with: "")
|
||||
|
||||
return ParsedQRData(
|
||||
type: .viber,
|
||||
title: "Viber",
|
||||
subtitle: "电话号码: \(phone)",
|
||||
icon: "bubble.left.and.bubble.right"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Snapchat
|
||||
private static func parseSnapchat(_ content: String) -> ParsedQRData {
|
||||
let username = content.replacingOccurrences(of: "snapchat://", with: "")
|
||||
|
||||
return ParsedQRData(
|
||||
type: .snapchat,
|
||||
title: "Snapchat",
|
||||
subtitle: "用户名: \(username)",
|
||||
icon: "camera.viewfinder"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析TikTok
|
||||
private static func parseTikTok(_ content: String) -> ParsedQRData {
|
||||
let username = content.components(separatedBy: "@").last?.replacingOccurrences(of: "/", with: "") ?? ""
|
||||
|
||||
return ParsedQRData(
|
||||
type: .tiktok,
|
||||
title: "TikTok",
|
||||
subtitle: "用户名: \(username)",
|
||||
icon: "music.mic"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析URL
|
||||
private static func parseURL(_ content: String) -> ParsedQRData {
|
||||
return ParsedQRData(
|
||||
type: .url,
|
||||
title: "网址链接",
|
||||
subtitle: content,
|
||||
icon: "link"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 解析Location
|
||||
private static func parseLocation(_ content: String) -> ParsedQRData {
|
||||
let coordinates = content.replacingOccurrences(of: "geo:", with: "")
|
||||
let coords = coordinates.components(separatedBy: ",")
|
||||
|
||||
let latitude = coords.first ?? ""
|
||||
let longitude = coords.count > 1 ? coords[1] : ""
|
||||
|
||||
let title = "地理位置"
|
||||
let subtitle = "纬度: \(latitude)\n经度: \(longitude)"
|
||||
|
||||
return ParsedQRData(
|
||||
type: .location,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: "location"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 验证URL
|
||||
private static func isValidURL(_ string: String) -> Bool {
|
||||
guard let url = URL(string: string) else { return false }
|
||||
return UIApplication.shared.canOpenURL(url)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue