You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
8.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import SwiftUI
import CoreData
import Photos
struct QRCodeSavedView: View {
@EnvironmentObject var languageManager: LanguageManager
let qrCodeImage: UIImage
let qrCodeContent: String
let qrCodeType: QRCodeType
let styleData: QRCodeStyleData?
let historyItem: HistoryItem?
@Environment(\.dismiss) private var dismiss
@EnvironmentObject var coreDataManager: CoreDataManager
@Environment(\.presentationMode) private var presentationMode
@State private var shouldReturnToRoot = false
@State private var shouldPopToRoot = false
@State private var showingShareSheet = false
@State private var showingAlert = false
@State private var alertMessage = ""
@State private var isSavingToPhotos = false
@State private var showingImageComposer = false
@State private var showingBackgroundImagePicker = false
@State private var selectedBackgroundImage: UIImage?
//
private let photoSaver = PhotoSaver()
var body: some View {
VStack(spacing: 30) {
//
qrCodeImageView
//
actionButtonsSection
Spacer()
}
.padding()
.navigationTitle("qr_code_saved_title".localized)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("return_home".localized) {
// ContentView
shouldPopToRoot = true
}
}
}
.sheet(isPresented: $showingShareSheet) {
ShareSheet(activityItems: [qrCodeImage])
}
.alert("tip".localized, isPresented: $showingAlert) {
Button("confirm".localized) { }
} message: {
Text(alertMessage)
}
.background(
NavigationLink(
destination: ContentView()
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true),
isActive: $shouldPopToRoot
) {
EmptyView()
}
)
.sheet(isPresented: $showingBackgroundImagePicker) {
ImagePicker(
onImageSelected: { image in
selectedBackgroundImage = image
showingImageComposer = true
},
shouldProcessImage: false
)
}
.sheet(isPresented: $showingImageComposer) {
if let backgroundImage = selectedBackgroundImage {
ImageComposerView(qrCodeImage: qrCodeImage, backgroundImage: backgroundImage)
}
}
}
// MARK: -
private var qrCodeImageView: some View {
VStack(spacing: 16) {
Image(uiImage: qrCodeImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 300, height: 300)
.cornerRadius(16)
.shadow(radius: 10)
Text("scan_this_qr_code".localized)
.font(.headline)
.foregroundColor(.secondary)
}
}
// MARK: -
private var actionButtonsSection: some View {
HStack(spacing: 12) {
//
Button(action: {
showingShareSheet = true
}) {
VStack(spacing: 8) {
Image(systemName: "square.and.arrow.up")
.font(.title2)
Text("share".localized)
.font(.caption)
.fontWeight(.medium)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(12)
}
//
Button(action: saveToPhotos) {
VStack(spacing: 8) {
if isSavingToPhotos {
ProgressView()
.scaleEffect(0.8)
.foregroundColor(.white)
} else {
Image(systemName: "photo")
.font(.title2)
}
Text(isSavingToPhotos ? "saving".localized : "save".localized)
.font(.caption)
.fontWeight(.medium)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(12)
}
.disabled(isSavingToPhotos)
//
Button(action: addToPhotos) {
VStack(spacing: 8) {
Image(systemName: "plus.rectangle.on.folder")
.font(.title2)
Text("add_to_picture".localized)
.font(.caption)
.fontWeight(.medium)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(Color.orange)
.foregroundColor(.white)
.cornerRadius(12)
}
}
}
// MARK: -
private func saveToPhotos() {
isSavingToPhotos = true
//
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized, .limited:
saveImageToPhotos()
case .notDetermined:
PHPhotoLibrary.requestAuthorization { newStatus in
DispatchQueue.main.async {
if newStatus == .authorized || newStatus == .limited {
self.saveImageToPhotos()
} else {
self.showPermissionAlert()
}
self.isSavingToPhotos = false
}
}
case .denied, .restricted:
DispatchQueue.main.async {
self.showPermissionAlert()
self.isSavingToPhotos = false
}
@unknown default:
DispatchQueue.main.async {
self.showPermissionAlert()
self.isSavingToPhotos = false
}
}
}
private func saveImageToPhotos() {
photoSaver.saveImage(qrCodeImage) { success, error in
DispatchQueue.main.async {
self.isSavingToPhotos = false
if success {
self.alertMessage = "qr_code_saved_to_photos".localized
} else {
self.alertMessage = String(format: "save_failed".localized, error?.localizedDescription ?? "unknown_error".localized)
}
self.showingAlert = true
}
}
}
private func showPermissionAlert() {
alertMessage = "photo_permission_required".localized
showingAlert = true
}
// MARK: -
private func addToPhotos() {
//
showingBackgroundImagePicker = true
}
}
// MARK: -
class PhotoSaver: NSObject {
func saveImage(_ image: UIImage, completion: @escaping (Bool, Error?) -> Void) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
self.completion = completion
}
private var completion: ((Bool, Error?) -> Void)?
@objc private func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
completion?(error == nil, error)
}
}
#Preview {
let sampleImage = UIImage(systemName: "qrcode") ?? UIImage()
let sampleStyleData = QRCodeStyleData(
foregroundColor: "black",
backgroundColor: "white",
dotType: "square",
eyeType: "square",
logo: nil,
hasCustomLogo: false,
customLogoFileName: nil
)
return QRCodeSavedView(
qrCodeImage: sampleImage,
qrCodeContent: "https://example.com",
qrCodeType: .url,
styleData: sampleStyleData,
historyItem: nil
)
.environmentObject(CoreDataManager())
.environmentObject(LanguageManager.shared)
}