@ -10,11 +10,8 @@ struct CreateQRCodeView: View {
// 从 类 型 选 择 界 面 传 入 的 参 数
let selectedQRCodeType : QRCodeType
// 通 用 内 容 输 入
@ State private var content = " "
@ State private var showingAlert = false
@ State private var alertMessage = " "
// 输 入 焦 点 , 确 保 进 入 页 面 自 动 弹 出 键 盘
@ FocusState private var isContentFieldFocused : Bool
// E m a i l 相 关 字 段
@ -23,12 +20,56 @@ struct CreateQRCodeView: View {
@ State private var emailBody = " "
@ State private var emailCc = " "
@ State private var emailBcc = " "
@ FocusState private var focusedEmailField : Email Field?
@ FocusState private var focusedEmailField : Email InputView. Email Field?
// E m a i l 字 段 枚 举
private enum EmailField : Hashable {
case address , subject , body , cc , bcc
}
// W i F i 相 关 字 段
@ State private var wifiSSID = " "
@ State private var wifiPassword = " "
@ State private var wifiEncryptionType : WiFiInputView . WiFiEncryptionType = . wpa2
@ FocusState private var focusedWiFiField : WiFiInputView . WiFiField ?
// 联 系 人 相 关 字 段
@ State private var contactFirstName = " "
@ State private var contactLastName = " "
@ State private var contactPhone = " "
@ State private var contactEmail = " "
@ State private var contactCompany = " "
@ State private var contactTitle = " "
@ State private var contactAddress = " "
@ State private var contactWebsite = " "
@ FocusState private var focusedContactField : ContactInputView . ContactField ?
// 位 置 相 关 字 段
@ State private var locationLatitude = " "
@ State private var locationLongitude = " "
@ State private var locationName = " "
@ FocusState private var focusedLocationField : LocationInputView . LocationField ?
// 日 历 相 关 字 段
@ State private var eventTitle = " "
@ State private var eventDescription = " "
@ State private var eventLocation = " "
@ State private var startDate = Date ( )
@ State private var endDate = Date ( ) . addingTimeInterval ( 3600 )
@ FocusState private var focusedCalendarField : CalendarInputView . CalendarField ?
// 社 交 平 台 相 关 字 段
@ State private var socialUsername = " "
@ State private var socialMessage = " "
@ FocusState private var focusedSocialField : SocialInputView . SocialField ?
// 电 话 相 关 字 段
@ State private var phoneNumber = " "
@ State private var phoneMessage = " "
@ FocusState private var focusedPhoneField : PhoneInputView . PhoneField ?
// U R L 相 关 字 段
@ State private var urlString = " "
@ FocusState private var isURLFieldFocused : Bool
// 通 用 状 态
@ State private var showingAlert = false
@ State private var alertMessage = " "
var body : some View {
VStack ( spacing : 0 ) {
@ -47,345 +88,92 @@ struct CreateQRCodeView: View {
Button ( " 确定 " ) { }
} message : { Text ( alertMessage ) }
. onAppear {
// 稍 延 迟 以 确 保 进 入 页 面 时 自 动 聚 焦
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.1 ) {
if selectedQRCodeType = = . mail {
focusedEmailField = . address
} else {
isContentFieldFocused = true
}
}
setupInitialFocus ( )
}
. onTapGesture {
// 点 击 外 部 关 闭 键 盘
if selectedQRCodeType = = . mail {
focusedEmailField = nil
} else {
isContentFieldFocused = false
}
hideKeyboard ( )
}
}
// MARK: - U I C o m p o n e n t s
private var emailInputSection : some View {
VStack ( spacing : 16 ) {
// E m a i l 地 址 ( 必 填 )
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Text ( " 邮箱地址 " )
. font ( . subheadline )
. foregroundColor ( . primary )
Text ( " * " )
. foregroundColor ( . red )
Spacer ( )
}
TextField ( " user@example.com " , text : $ emailAddress )
. textFieldStyle ( RoundedBorderTextFieldStyle ( ) )
. keyboardType ( . emailAddress )
. autocapitalization ( . none )
. focused ( $ focusedEmailField , equals : . address )
}
// 主 题 ( 必 填 )
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Text ( " 主题 " )
. font ( . subheadline )
. foregroundColor ( . primary )
Text ( " * " )
. foregroundColor ( . red )
Spacer ( )
}
TextField ( " 邮件主题 " , text : $ emailSubject )
. textFieldStyle ( RoundedBorderTextFieldStyle ( ) )
. focused ( $ focusedEmailField , equals : . subject )
}
// 正 文 ( 必 填 )
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Text ( " 正文 " )
. font ( . subheadline )
. foregroundColor ( . primary )
Text ( " * " )
. foregroundColor ( . red )
Spacer ( )
}
ZStack {
TextEditor ( text : $ emailBody )
. frame ( minHeight : 120 )
. padding ( 8 )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 8 )
. overlay (
RoundedRectangle ( cornerRadius : 8 )
. stroke ( focusedEmailField = = . body ? Color . blue : Color ( . systemGray4 ) , lineWidth : 1 )
)
. focused ( $ focusedEmailField , equals : . body )
. onChange ( of : emailBody ) { newValue in
// 限 制 最 大 字 符 数 为 1 2 0 0
if newValue . count > 1200 {
emailBody = String ( newValue . prefix ( 1200 ) )
}
}
// 占 位 符 文 本
if emailBody . isEmpty && focusedEmailField != . body {
VStack {
HStack {
Text ( " 输入邮件正文内容... " )
. foregroundColor ( . secondary )
. font ( . body )
Spacer ( )
}
Spacer ( )
}
. padding ( 16 )
. allowsHitTesting ( false )
. frame ( maxWidth : . infinity , maxHeight : . infinity , alignment : . topLeading )
}
}
// 字 符 计 数
HStack {
Spacer ( )
Text ( " \( emailBody . count ) /1200 " )
. font ( . caption )
. foregroundColor ( emailBody . count >= 1200 ? . orange : . secondary )
}
}
// C C 地 址 ( 可 选 )
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Text ( " 抄送地址 " )
. font ( . subheadline )
. foregroundColor ( . primary )
Spacer ( )
}
TextField ( " cc@example.com " , text : $ emailCc )
. textFieldStyle ( RoundedBorderTextFieldStyle ( ) )
. keyboardType ( . emailAddress )
. autocapitalization ( . none )
. focused ( $ focusedEmailField , equals : . cc )
}
// B C C 地 址 ( 可 选 )
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Text ( " 密送地址 " )
. font ( . subheadline )
. foregroundColor ( . primary )
Spacer ( )
}
TextField ( " bcc@example.com " , text : $ emailBcc )
. textFieldStyle ( RoundedBorderTextFieldStyle ( ) )
. keyboardType ( . emailAddress )
. autocapitalization ( . none )
. focused ( $ focusedEmailField , equals : . bcc )
}
}
. toolbar {
ToolbarItemGroup ( placement : . keyboard ) {
Spacer ( )
Button ( " 完成 " ) {
focusedEmailField = nil
}
. foregroundColor ( . blue )
. font ( . system ( size : 16 , weight : . medium ) )
}
}
}
private var inputAndPreviewSection : some View {
ScrollView {
VStack ( spacing : 24 ) {
// 输 入 提 示
VStack ( spacing : 12 ) {
HStack {
Image ( systemName : " info.circle " )
. font ( . caption )
. foregroundColor ( . blue )
Text ( getContentHint ( ) )
. font ( . caption )
. foregroundColor ( . secondary )
. lineLimit ( nil )
Spacer ( )
}
. padding ( . horizontal , 12 )
. padding ( . vertical , 8 )
. background (
RoundedRectangle ( cornerRadius : 8 )
. fill ( Color . blue . opacity ( 0.1 ) )
)
}
InputHintView . info (
hint : getContentHint ( )
)
. padding ( . horizontal , 20 )
// 内 容 输 入 区 域
VStack ( spacing : 16 ) {
HStack {
Text ( selectedQRCodeType = = . mail ? " 邮件信息 " : " 输入内容 " )
. font ( . headline )
. foregroundColor ( . primary )
Spacer ( )
}
InputTitleView . required (
selectedQRCodeType = = . mail ? " 邮件信息 " : " 输入内容 " ,
icon : getInputIcon ( )
)
. padding ( . horizontal , 20 )
if selectedQRCodeType = = . mail {
// E m a i l 专 用 输 入 界 面
emailInputSection
} else {
// 通 用 输 入 界 面
VStack ( spacing : 8 ) {
ZStack {
// 输 入 框 主 体
TextEditor ( text : $ content )
. frame ( minHeight : 120 )
. padding ( 8 )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 8 )
. overlay (
RoundedRectangle ( cornerRadius : 8 )
. stroke ( isContentFieldFocused ? Color . blue : Color ( . systemGray4 ) , lineWidth : 1 )
)
. focused ( $ isContentFieldFocused )
. onChange ( of : content ) { newValue in
// 限 制 最 大 字 符 数 为 1 5 0
if newValue . count > 150 {
content = String ( newValue . prefix ( 150 ) )
}
}
. toolbar {
ToolbarItemGroup ( placement : . keyboard ) {
Spacer ( )
Button ( " 完成 " ) {
isContentFieldFocused = false
}
. foregroundColor ( . blue )
. font ( . system ( size : 16 , weight : . medium ) )
}
}
// 占 位 符 文 本 - 左 上 角 对 齐
if content . isEmpty && ! isContentFieldFocused {
VStack {
HStack {
Text ( getPlaceholderText ( ) )
. foregroundColor ( . secondary )
. font ( . body )
Spacer ( )
}
Spacer ( )
}
. padding ( 16 )
. allowsHitTesting ( false )
. frame ( maxWidth : . infinity , maxHeight : . infinity , alignment : . topLeading )
}
}
// 字 符 计 数 和 限 制 提 示 - 输 入 框 底 下
HStack {
Spacer ( ) // P u s h e s c o n t e n t t o t h e r i g h t
VStack ( alignment : . trailing , spacing : 4 ) {
// 字 符 限 制 提 示
if content . count >= 150 {
HStack ( spacing : 4 ) {
Image ( systemName : " exclamationmark.triangle " )
. font ( . caption )
. foregroundColor ( . orange )
Text ( " 已达到最大字符数 " )
. font ( . caption )
. foregroundColor ( . orange )
}
} else if content . count >= 140 {
HStack ( spacing : 4 ) {
Image ( systemName : " info.circle " )
. font ( . caption )
. foregroundColor ( . blue )
Text ( " 接近字符限制 " )
. font ( . caption )
. foregroundColor ( . blue )
}
}
// 字 符 计 数
Text ( " \( content . count ) /150 " )
. font ( . caption )
. foregroundColor ( getCharacterCountColor ( ) )
}
}
}
}
// 使 用 I n p u t C o m p o n e n t F a c t o r y 动 态 选 择 输 入 组 件
InputComponentFactory . createInputComponent (
for : selectedQRCodeType ,
content : $ content ,
emailAddress : $ emailAddress ,
emailSubject : $ emailSubject ,
emailBody : $ emailBody ,
emailCc : $ emailCc ,
emailBcc : $ emailBcc ,
focusedEmailField : _focusedEmailField ,
isContentFieldFocused : _isContentFieldFocused ,
ssid : $ wifiSSID ,
password : $ wifiPassword ,
encryptionType : $ wifiEncryptionType ,
focusedWiFiField : _focusedWiFiField ,
firstName : $ contactFirstName ,
lastName : $ contactLastName ,
phone : $ contactPhone ,
email : $ contactEmail ,
company : $ contactCompany ,
title : $ contactTitle ,
address : $ contactAddress ,
website : $ contactWebsite ,
focusedContactField : _focusedContactField ,
latitude : $ locationLatitude ,
longitude : $ locationLongitude ,
locationName : $ locationName ,
focusedLocationField : _focusedLocationField ,
eventTitle : $ eventTitle ,
eventDescription : $ eventDescription ,
startDate : $ startDate ,
endDate : $ endDate ,
location : $ eventLocation ,
focusedCalendarField : _focusedCalendarField ,
username : $ socialUsername ,
message : $ socialMessage ,
focusedSocialField : _focusedSocialField ,
phoneNumber : $ phoneNumber ,
phoneMessage : $ phoneMessage ,
focusedPhoneField : _focusedPhoneField ,
url : $ urlString ,
isUrlFieldFocused : _isURLFieldFocused
)
. padding ( . horizontal , 20 )
}
. padding ( . horizontal , 20 )
// 预 览 区 域
if selectedQRCodeType = = . mail ? ( ! emailAddress . isEmpty && ! emailSubject . isEmpty && ! emailBody . isEmpty ) : ! content . isEmpty {
if canCreateQRCode ( ) {
VStack ( spacing : 16 ) {
HStack {
Text ( " 预览 " )
. font ( . headline )
. foregroundColor ( . primary )
Spacer ( )
}
InputTitleView . required ( " 预览 " , icon : " eye " )
. padding ( . horizontal , 20 )
// 二 维 码 预 览 卡 片
VStack ( spacing : 16 ) {
// 二 维 码 图 片
if let qrImage = generateQRCodeImage ( ) {
Image ( uiImage : qrImage )
. interpolation ( . none )
. resizable ( )
. scaledToFit ( )
. frame ( width : 200 , height : 200 )
. background ( Color . white )
. cornerRadius ( 12 )
. shadow ( color : . black . opacity ( 0.1 ) , radius : 8 , x : 0 , y : 4 )
}
// 内 容 预 览 卡 片
VStack ( alignment : . leading , spacing : 8 ) {
HStack {
Text ( " 内容 " )
. font ( . caption )
. foregroundColor ( . secondary )
Spacer ( )
Text ( selectedQRCodeType . displayName )
. font ( . caption )
. padding ( . horizontal , 6 )
. padding ( . vertical , 2 )
. background ( Color . orange . opacity ( 0.1 ) )
. foregroundColor ( . orange )
. cornerRadius ( 4 )
}
Text ( formatContentForQRCodeType ( ) )
. font ( . body )
. foregroundColor ( . primary )
. textSelection ( . enabled )
}
. padding ( )
. background ( Color ( . systemGray6 ) )
. cornerRadius ( 8 )
}
. padding ( )
. background ( Color ( . systemBackground ) )
. cornerRadius ( 12 )
. shadow ( color : . black . opacity ( 0.05 ) , radius : 4 , x : 0 , y : 2 )
// 使 用 Q R C o d e P r e v i e w V i e w 组 件
QRCodePreviewView (
qrCodeImage : generateQRCodeImage ( ) ,
formattedContent : formatContentForQRCodeType ( ) ,
qrCodeType : selectedQRCodeType
)
. padding ( . horizontal , 20 )
}
. padding ( . horizontal , 20 )
}
Spacer ( minLength : 100 )
@ -397,133 +185,106 @@ struct CreateQRCodeView: View {
// MARK: - H e l p e r M e t h o d s
private func canCreateQRCode ( ) -> Bool {
switch selectedQRCodeType {
case . mail :
// E m a i l 类 型 : 邮 箱 地 址 、 主 题 、 正 文 为 必 填
return ! emailAddress . isEmpty && ! emailSubject . isEmpty && ! emailBody . isEmpty
default :
// 其 他 类 型 : 内 容 不 能 为 空
return ! content . isEmpty
}
}
private func getCharacterCountColor ( ) -> Color {
if content . count >= 150 {
return . orange
} else if content . count >= 140 {
return . blue
} else {
return . secondary
private func setupInitialFocus ( ) {
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.1 ) {
switch selectedQRCodeType {
case . mail :
focusedEmailField = . address
case . wifi :
focusedWiFiField = . ssid
case . vcard , . mecard :
focusedContactField = . firstName
case . location :
focusedLocationField = . latitude
case . calendar :
focusedCalendarField = . title
case . instagram , . facebook , . spotify , . twitter , . snapchat , . tiktok :
focusedSocialField = . username
case . phone , . sms :
focusedPhoneField = . phoneNumber
case . url :
isURLFieldFocused = true
default :
isContentFieldFocused = true
}
}
}
private func getContentHint ( ) -> String {
private func hideKeyboard( ) {
switch selectedQRCodeType {
case . text :
return " 输入任意文本内容 "
case . url :
return " 输入网址, 如: https://www.example.com "
case . mail :
return " 填写邮件信息,邮箱地址、主题、正文为必填项 "
case . phone :
return " 输入电话号码,如:+86 138 0013 8000 "
case . sms :
return " 输入短信内容, 如: Hello World "
focusedEmailField = nil
case . wifi :
return " 输入WiFi信息, 如: SSID:MyWiFi,Password:12345678 "
case . vcard :
return " 输入联系人信息 "
case . mecard :
return " 输入联系人信息(简化版) "
focusedWiFiField = nil
case . vcard , . mecard :
focusedContactField = nil
case . location :
return " 输入地理位置, 如: 40.7128,-74.0060 "
focusedLocationField = nil
case . calendar :
return " 输入日历事件信息 "
case . instagram :
return " 输入Instagram用户名或链接 "
case . facebook :
return " 输入Facebook用户名或链接 "
case . spotify :
return " 输入Spotify歌曲或播放列表链接 "
case . twitter :
return " 输入Twitter用户名或链接 "
case . whatsapp :
return " 输入WhatsApp消息内容 "
case . viber :
return " 输入Viber消息内容 "
case . snapchat :
return " 输入Snapchat用户名 "
case . tiktok :
return " 输入TikTok用户名或链接 "
focusedCalendarField = nil
case . instagram , . facebook , . spotify , . twitter , . snapchat , . tiktok :
focusedSocialField = nil
case . phone , . sms :
focusedPhoneField = nil
case . url :
isURLFieldFocused = false
default :
isContentFieldFocused = false
}
}
private func getPlaceholderText ( ) -> String {
private func getInputIcon ( ) -> String {
switch selectedQRCodeType {
case . mail : return " envelope "
case . wifi : return " wifi "
case . vcard , . mecard : return " person "
case . location : return " location "
case . calendar : return " calendar "
case . instagram , . facebook , . spotify , . twitter , . snapchat , . tiktok : return " globe "
case . phone , . sms : return " phone "
case . url : return " link "
default : return " textformat "
}
}
private func canCreateQRCode ( ) -> Bool {
switch selectedQRCodeType {
case . text :
return " 输入任意文本内容... "
case . url :
return " 输入网址... "
case . mail :
return " 输入邮件内容... "
case . phone :
return " 输入电话号码... "
case . sms :
return " 输入短信内容... "
return ! emailAddress . isEmpty && ! emailSubject . isEmpty && ! emailBody . isEmpty
case . wifi :
return " 输入WiFi信息... "
case . vcard :
return " 输入联系人信息... "
case . mecard :
return " 输入联系人信息... "
return ! wifiSSID . isEmpty
case . vcard , . mecard :
return ! contactFirstName . isEmpty || ! contactLastName . isEmpty
case . location :
return " 输入地理位置... "
return ! locationLatitude . isEmpty && ! locationLongitude . isEmpty
case . calendar :
return " 输入日历事件信息... "
case . instagram :
return " 输入Instagram信息... "
case . facebook :
return " 输入Facebook信息... "
case . spotify :
return " 输入Spotify信息... "
case . twitter :
return " 输入Twitter信息... "
case . whatsapp :
return " 输入WhatsApp信息... "
case . viber :
return " 输入Viber信息... "
case . snapchat :
return " 输入Snapchat信息... "
case . tiktok :
return " 输入TikTok信息... "
return ! eventTitle . isEmpty
case . instagram , . facebook , . spotify , . twitter , . snapchat , . tiktok :
return ! socialUsername . isEmpty
case . phone , . sms :
return ! phoneNumber . isEmpty
case . url :
return ! urlString . isEmpty
default :
return ! content . isEmpty
}
}
private func getContentHint ( ) -> String {
InputComponentFactory . getPlaceholderText ( for : selectedQRCodeType )
}
private func generateQRCodeImage ( ) -> UIImage ? {
// 根 据 二 维 码 类 型 检 查 内 容 是 否 为 空
let hasContent : Bool
switch selectedQRCodeType {
case . mail :
hasContent = ! emailAddress . isEmpty && ! emailSubject . isEmpty && ! emailBody . isEmpty
default :
hasContent = ! content . isEmpty
}
guard canCreateQRCode ( ) else { return nil }
guard hasContent else { return nil }
// 根 据 二 维 码 类 型 格 式 化 内 容
let formattedContent = formatContentForQRCodeType ( )
// 生 成 二 维 码
let data = formattedContent . data ( using : . utf8 )
let qrFilter = CIFilter . qrCodeGenerator ( )
qrFilter . setValue ( data , forKey : " inputMessage " )
qrFilter . setValue ( " H " , forKey : " inputCorrectionLevel " ) // 高 纠 错 级 别
qrFilter . setValue ( " H " , forKey : " inputCorrectionLevel " )
guard let outputImage = qrFilter . outputImage else { return nil }
// 转 换 为 U I I m a g e
let context = CIContext ( )
guard let cgImage = context . createCGImage ( outputImage , from : outputImage . extent ) else { return nil }
@ -535,7 +296,7 @@ struct CreateQRCodeView: View {
case . text :
return content
case . url :
return content. hasPrefix ( " http " ) ? content : " https:// \( content ) "
return urlString. hasPrefix ( " http " ) ? urlString : " https:// \( urlString ) "
case . mail :
var mailtoURL = " mailto: \( emailAddress ) "
var queryParams : [ String ] = [ ]
@ -562,35 +323,97 @@ struct CreateQRCodeView: View {
return mailtoURL
case . phone :
return " tel: \( content ) "
return " tel: \( phoneNumber ) "
case . sms :
return " sms: \( content ) "
let smsContent = phoneMessage . isEmpty ? " Hello " : phoneMessage
return " sms: \( phoneNumber ) : \( smsContent ) "
case . wifi :
return " WIFI:T: WPA;S:\( content ) ;P:password ;;"
return " WIFI:T: \( wifiEncryptionType . rawValue ) ;S: \( wifiSSID ) ;P: \( wifiPassword ) ;;"
case . vcard :
return " BEGIN:VCARD \n VERSION:3.0 \n FN: \( content ) \n END:VCARD "
var vcard = " BEGIN:VCARD \n VERSION:3.0 \n "
if ! contactFirstName . isEmpty || ! contactLastName . isEmpty {
vcard += " FN: \( contactFirstName ) \( contactLastName ) \n "
}
if ! contactPhone . isEmpty {
vcard += " TEL: \( contactPhone ) \n "
}
if ! contactEmail . isEmpty {
vcard += " EMAIL: \( contactEmail ) \n "
}
if ! contactCompany . isEmpty {
vcard += " ORG: \( contactCompany ) \n "
}
if ! contactTitle . isEmpty {
vcard += " TITLE: \( contactTitle ) \n "
}
if ! contactAddress . isEmpty {
vcard += " ADR: \( contactAddress ) \n "
}
if ! contactWebsite . isEmpty {
vcard += " URL: \( contactWebsite ) \n "
}
vcard += " END:VCARD "
return vcard
case . mecard :
return " MECARD:N: \( content ) ;; "
var mecard = " MECARD: "
if ! contactFirstName . isEmpty || ! contactLastName . isEmpty {
mecard += " N: \( contactLastName ) , \( contactFirstName ) ; "
}
if ! contactPhone . isEmpty {
mecard += " TEL: \( contactPhone ) ; "
}
if ! contactEmail . isEmpty {
mecard += " EMAIL: \( contactEmail ) ; "
}
if ! contactCompany . isEmpty {
mecard += " ORG: \( contactCompany ) ; "
}
if ! contactAddress . isEmpty {
mecard += " ADR: \( contactAddress ) ; "
}
if ! contactWebsite . isEmpty {
mecard += " URL: \( contactWebsite ) ; "
}
mecard += " ; "
return mecard
case . location :
return " geo: \( content ) "
let coords = " \( locationLatitude ) , \( locationLongitude ) "
return locationName . isEmpty ? " geo: \( coords ) " : " geo: \( coords ) ?q= \( locationName ) "
case . calendar :
return " BEGIN:VEVENT \n SUMMARY: \( content ) \n END:VEVENT "
let dateFormatter = DateFormatter ( )
dateFormatter . dateFormat = " yyyyMMdd'T'HHmmss'Z' "
dateFormatter . timeZone = TimeZone ( abbreviation : " UTC " )
var ical = " BEGIN:VEVENT \n "
ical += " SUMMARY: \( eventTitle ) \n "
if ! eventDescription . isEmpty {
ical += " DESCRIPTION: \( eventDescription ) \n "
}
if ! eventLocation . isEmpty {
ical += " LOCATION: \( eventLocation ) \n "
}
ical += " DTSTART: \( dateFormatter . string ( from : startDate ) ) \n "
ical += " DTEND: \( dateFormatter . string ( from : endDate ) ) \n "
ical += " END:VEVENT "
return ical
case . instagram :
return " https://instagram.com/ \( content ) "
return " https://instagram.com/ \( socialUsername ) "
case . facebook :
return " https://facebook.com/ \( content ) "
return " https://facebook.com/ \( socialUsername ) "
case . spotify :
return content . hasPrefix ( " http " ) ? content : " https://open.spotify.com/track/ \( content ) "
return socialUsername. hasPrefix ( " http " ) ? socialUsername : " https://open.spotify.com/track/ \( socialUsername ) "
case . twitter :
return " https://twitter.com/ \( content ) "
return " https://twitter.com/ \( socialUsername ) "
case . whatsapp :
return " https://wa.me/ \( content ) "
let message = socialMessage . isEmpty ? " Hello " : socialMessage
return " https://wa.me/ \( socialUsername ) ?text= \( message . addingPercentEncoding ( withAllowedCharacters : . urlQueryAllowed ) ? ? message ) "
case . viber :
return " viber://chat?number= \( content ) "
let message = socialMessage . isEmpty ? " Hello " : socialMessage
return " viber://chat?number= \( socialUsername ) &text= \( message . addingPercentEncoding ( withAllowedCharacters : . urlQueryAllowed ) ? ? message ) "
case . snapchat :
return " https://snapchat.com/add/ \( content ) "
return " https://snapchat.com/add/ \( socialUsername ) "
case . tiktok :
return " https://tiktok.com/@ \( content ) "
return " https://tiktok.com/@ \( socialUsername ) "
}
}
@ -615,6 +438,20 @@ struct CreateQRCodeView: View {
mailContent += " \n 密送: \( emailBcc ) "
}
historyItem . content = mailContent
case . wifi :
historyItem . content = " WiFi: \( wifiSSID ) ( \( wifiEncryptionType . displayName ) ) "
case . vcard , . mecard :
historyItem . content = " 联系人: \( contactFirstName ) \( contactLastName ) "
case . location :
historyItem . content = " 位置: \( locationLatitude ) , \( locationLongitude ) "
case . calendar :
historyItem . content = " 事件: \( eventTitle ) "
case . instagram , . facebook , . spotify , . twitter , . snapchat , . tiktok :
historyItem . content = " \( selectedQRCodeType . displayName ) : \( socialUsername ) "
case . phone , . sms :
historyItem . content = " 电话: \( phoneNumber ) "
case . url :
historyItem . content = " URL: \( urlString ) "
default :
historyItem . content = content
}