@ -2,6 +2,32 @@ import SwiftUI
import QRCode
import CoreData
// MARK: - 标 签 类 型 枚 举
enum TabType : String , CaseIterable {
case colors = " colors "
case dots = " dots "
case eyes = " eyes "
case logos = " logos "
var displayName : String {
switch self {
case . colors : return " 颜色 "
case . dots : return " 点类型 "
case . eyes : return " 眼睛 "
case . logos : return " Logo "
}
}
var iconName : String {
switch self {
case . colors : return " paintpalette "
case . dots : return " circle.grid.3x3 "
case . eyes : return " eye "
case . logos : return " photo "
}
}
}
// MARK: - 自 定 义 二 维 码 样 式 界 面
struct QRCodeStyleView : View {
let qrCodeContent : String
@ -25,6 +51,9 @@ struct QRCodeStyleView: View {
@ State private var qrCodeImage : UIImage ?
@ State private var isLoading = false
// 选 中 的 标 签 类 型
@ State private var selectedTabType : TabType = . colors
// 创 建 Q R C o d e 文 档
private func createQRCodeDocument ( ) -> QRCode . Document {
let d = try ! QRCode . Document ( engine : QRCodeEngineExternal ( ) )
@ -95,6 +124,69 @@ struct QRCodeStyleView: View {
// MARK: - 样 式 选 择 区 域
private var styleSelectionSection : some View {
VStack ( spacing : 0 ) {
// 标 签 类 型 选 择
tabTypeSelection
// 内 容 区 域
contentArea
}
. background ( Color ( . systemGroupedBackground ) )
}
// MARK: - 标 签 类 型 选 择
private var tabTypeSelection : some View {
HStack ( spacing : 0 ) {
ForEach ( TabType . allCases , id : \ . self ) { tabType in
Button ( action : {
selectedTabType = tabType
} ) {
VStack ( spacing : 4 ) {
Image ( systemName : tabType . iconName )
. font ( . system ( size : 20 ) )
. foregroundColor ( selectedTabType = = tabType ? . blue : . gray )
Text ( tabType . displayName )
. font ( . caption )
. foregroundColor ( selectedTabType = = tabType ? . blue : . gray )
}
. frame ( maxWidth : . infinity )
. padding ( . vertical , 12 )
. background (
Rectangle ( )
. fill ( selectedTabType = = tabType ? Color . blue . opacity ( 0.1 ) : Color . clear )
)
}
}
}
. background ( Color ( . systemBackground ) )
. overlay (
Rectangle ( )
. frame ( height : 1 )
. foregroundColor ( Color ( . separator ) ) ,
alignment : . bottom
)
}
// MARK: - 内 容 区 域
private var contentArea : some View {
Group {
switch selectedTabType {
case . colors :
colorsContent
case . dots :
dotsContent
case . eyes :
eyesContent
case . logos :
logosContent
}
}
. frame ( maxHeight : 400 )
}
// MARK: - 颜 色 内 容
private var colorsContent : some View {
ScrollView {
VStack ( spacing : 24 ) {
// 前 景 色 选 择
@ -110,59 +202,21 @@ struct QRCodeStyleView: View {
colors : QRCodeColor . backgroundColors ,
selectedColor : $ selectedBackgroundColor
)
// 点 类 型 选 择
dotTypeSelectionSection
// 眼 睛 类 型 选 择
eyeTypeSelectionSection
// L o g o 选 择
logoSelectionSection
}
. padding ( )
}
. background ( Color ( . systemGroupedBackground ) )
}
// MARK: - 颜 色 选 择 区 域
private func colorSelectionSection (
title : String ,
colors : [ QRCodeColor ] ,
selectedColor : Binding < QRCodeColor >
) -> some View {
VStack ( alignment : . leading , spacing : 12 ) {
Text ( title )
. font ( . headline )
. foregroundColor ( . primary )
LazyVGrid ( columns : Array ( repeating : GridItem ( . flexible ( ) ) , count : 6 ) , spacing : 12 ) {
ForEach ( colors , id : \ . self ) { color in
Button ( action : {
selectedColor . wrappedValue = color
} ) {
RoundedRectangle ( cornerRadius : 8 )
. fill ( color . color )
. frame ( height : 40 )
. overlay (
RoundedRectangle ( cornerRadius : 8 )
. stroke ( selectedColor . wrappedValue = = color ? Color . blue : Color . clear , lineWidth : 3 )
)
}
}
}
}
}
// MARK: - 点 类 型 选 择 区 域
private var dotTypeSelectionSection : some View {
VStack ( alignment : . leading , spacing : 12 ) {
Text ( " 点类型 " )
. font ( . headline )
. foregroundColor ( . primary )
// MARK: - 点 类 型 内 容
private var dotsContent : some View {
ScrollView {
VStack ( spacing : 16 ) {
Text ( " 选择点类型 " )
. font ( . title2 )
. fontWeight ( . bold )
. padding ( . top )
ScrollView ( . horizontal , showsIndicators : false ) {
HStack ( spacing : 12 ) {
LazyVGrid ( columns : Array ( repeating : GridItem ( . flexible ( ) ) , count : 3 ) , spacing : 16 ) {
ForEach ( QRCodeDotType . allCases , id : \ . self ) { dotType in
Button ( action : {
selectedDotType = dotType
@ -172,16 +226,16 @@ struct QRCodeStyleView: View {
Image ( uiImage : image )
. resizable ( )
. aspectRatio ( contentMode : . fit )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. background ( Color . white )
. cornerRadius ( 8 )
. cornerRadius ( 12 )
} else {
RoundedRectangle ( cornerRadius : 8 )
RoundedRectangle ( cornerRadius : 12 )
. fill ( Color . gray . opacity ( 0.3 ) )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. overlay (
Text ( " ? " )
. font ( . caption )
. font ( . title2 )
. foregroundColor ( . secondary )
)
}
@ -189,14 +243,15 @@ struct QRCodeStyleView: View {
Text ( dotType . displayName )
. font ( . caption )
. foregroundColor ( . primary )
. multilineTextAlignment ( . center )
}
. padding ( 8 )
. padding ( 12 )
. background (
RoundedRectangle ( cornerRadius : 1 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. fill ( selectedDotType = = dotType ? Color . blue . opacity ( 0.1 ) : Color . clear )
. overlay (
RoundedRectangle ( cornerRadius : 1 2 )
. stroke ( selectedDotType = = dotType ? Color . blue : Color . clear , lineWidth : 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. stroke ( selectedDotType = = dotType ? Color . blue : Color . clear , lineWidth : 3 )
)
)
}
@ -207,15 +262,16 @@ struct QRCodeStyleView: View {
}
}
// MARK: - 眼 睛 类 型 选 择 区 域
private var eyeTypeSelectionSection : some View {
VStack ( alignment : . leading , spacing : 12 ) {
Text ( " 眼睛类型 " )
. font ( . headline )
. foregroundColor ( . primary )
// MARK: - 眼 睛 类 型 内 容
private var eyesContent : some View {
ScrollView {
VStack ( spacing : 16 ) {
Text ( " 选择眼睛类型 " )
. font ( . title2 )
. fontWeight ( . bold )
. padding ( . top )
ScrollView ( . horizontal , showsIndicators : false ) {
HStack ( spacing : 12 ) {
LazyVGrid ( columns : Array ( repeating : GridItem ( . flexible ( ) ) , count : 3 ) , spacing : 16 ) {
ForEach ( QRCodeEyeType . allCases , id : \ . self ) { eyeType in
Button ( action : {
selectedEyeType = eyeType
@ -225,16 +281,16 @@ struct QRCodeStyleView: View {
Image ( uiImage : image )
. resizable ( )
. aspectRatio ( contentMode : . fit )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. background ( Color . white )
. cornerRadius ( 8 )
. cornerRadius ( 12 )
} else {
RoundedRectangle ( cornerRadius : 8 )
RoundedRectangle ( cornerRadius : 12 )
. fill ( Color . gray . opacity ( 0.3 ) )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. overlay (
Text ( " ? " )
. font ( . caption )
. font ( . title2 )
. foregroundColor ( . secondary )
)
}
@ -242,14 +298,15 @@ struct QRCodeStyleView: View {
Text ( eyeType . displayName )
. font ( . caption )
. foregroundColor ( . primary )
. multilineTextAlignment ( . center )
}
. padding ( 8 )
. padding ( 12 )
. background (
RoundedRectangle ( cornerRadius : 1 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. fill ( selectedEyeType = = eyeType ? Color . blue . opacity ( 0.1 ) : Color . clear )
. overlay (
RoundedRectangle ( cornerRadius : 1 2 )
. stroke ( selectedEyeType = = eyeType ? Color . blue : Color . clear , lineWidth : 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. stroke ( selectedEyeType = = eyeType ? Color . blue : Color . clear , lineWidth : 3 )
)
)
}
@ -260,40 +317,42 @@ struct QRCodeStyleView: View {
}
}
// MARK: - L o g o 选 择 区 域
private var logoSelectionSection : some View {
VStack ( alignment : . leading , spacing : 12 ) {
Text ( " Logo " )
. font ( . headline )
. foregroundColor ( . primary )
// MARK: - L o g o 内 容
private var logosContent : some View {
ScrollView {
VStack ( spacing : 16 ) {
Text ( " 选择Logo " )
. font ( . title2 )
. fontWeight ( . bold )
. padding ( . top )
ScrollView ( . horizontal , showsIndicators : false ) {
HStack ( spacing : 12 ) {
LazyVGrid ( columns : Array ( repeating : GridItem ( . flexible ( ) ) , count : 3 ) , spacing : 16 ) {
// 无 L o g o 选 项
Button ( action : {
selectedLogo = nil
} ) {
VStack ( spacing : 8 ) {
RoundedRectangle ( cornerRadius : 8 )
RoundedRectangle ( cornerRadius : 12 )
. fill ( Color . gray . opacity ( 0.3 ) )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. overlay (
Text ( " 无 " )
. font ( . caption )
. font ( . title2 )
. foregroundColor ( . secondary )
)
Text ( " 无Logo " )
. font ( . caption )
. foregroundColor ( . primary )
. multilineTextAlignment ( . center )
}
. padding ( 8 )
. padding ( 12 )
. background (
RoundedRectangle ( cornerRadius : 1 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. fill ( selectedLogo = = nil ? Color . blue . opacity ( 0.1 ) : Color . clear )
. overlay (
RoundedRectangle ( cornerRadius : 1 2 )
. stroke ( selectedLogo = = nil ? Color . blue : Color . clear , lineWidth : 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. stroke ( selectedLogo = = nil ? Color . blue : Color . clear , lineWidth : 3 )
)
)
}
@ -308,16 +367,16 @@ struct QRCodeStyleView: View {
Image ( uiImage : image )
. resizable ( )
. aspectRatio ( contentMode : . fit )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. background ( Color . white )
. cornerRadius ( 8 )
. cornerRadius ( 12 )
} else {
RoundedRectangle ( cornerRadius : 8 )
RoundedRectangle ( cornerRadius : 12 )
. fill ( Color . gray . opacity ( 0.3 ) )
. frame ( width : 40, height : 4 0)
. frame ( width : 60, height : 6 0)
. overlay (
Text ( " ? " )
. font ( . caption )
. font ( . title2 )
. foregroundColor ( . secondary )
)
}
@ -325,14 +384,15 @@ struct QRCodeStyleView: View {
Text ( logo . displayName )
. font ( . caption )
. foregroundColor ( . primary )
. multilineTextAlignment ( . center )
}
. padding ( 8 )
. padding ( 12 )
. background (
RoundedRectangle ( cornerRadius : 1 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. fill ( selectedLogo = = logo ? Color . blue . opacity ( 0.1 ) : Color . clear )
. overlay (
RoundedRectangle ( cornerRadius : 1 2 )
. stroke ( selectedLogo = = logo ? Color . blue : Color . clear , lineWidth : 2 )
RoundedRectangle ( cornerRadius : 1 6 )
. stroke ( selectedLogo = = logo ? Color . blue : Color . clear , lineWidth : 3 )
)
)
}
@ -343,6 +403,37 @@ struct QRCodeStyleView: View {
}
}
// MARK: - 颜 色 选 择 区 域
private func colorSelectionSection (
title : String ,
colors : [ QRCodeColor ] ,
selectedColor : Binding < QRCodeColor >
) -> some View {
VStack ( alignment : . leading , spacing : 12 ) {
Text ( title )
. font ( . headline )
. foregroundColor ( . primary )
LazyVGrid ( columns : Array ( repeating : GridItem ( . flexible ( ) ) , count : 6 ) , spacing : 12 ) {
ForEach ( colors , id : \ . self ) { color in
Button ( action : {
selectedColor . wrappedValue = color
} ) {
RoundedRectangle ( cornerRadius : 8 )
. fill ( color . color )
. frame ( height : 40 )
. overlay (
RoundedRectangle ( cornerRadius : 8 )
. stroke ( selectedColor . wrappedValue = = color ? Color . blue : Color . clear , lineWidth : 3 )
)
}
}
}
}
}
// MARK: - 保 存 二 维 码
private func saveQRCode ( ) {
guard let qrCodeImage = qrCodeImage else { return }