taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 96774c21008b594cdb1a3a949fffc26b2c2b5829
parent 4fb00bd5819f077ca046638a6d62d9075f698b22
Author: Marc Stibane <marc@taler.net>
Date:   Sat, 31 Oct 2026 17:46:17 +0100

separate QR generation from view

Diffstat:
MTalerCommon/HelperViews/QRGeneratorView.swift | 48+++++++++++++++++-------------------------------
MTalerWallet1/Views/HelperViews/QRCodeDetailView.swift | 7++++++-
MTalerWallet1/Views/OIM/OIMp2pReadyView.swift | 13++++++++++---
MTalerWallet1/Views/Transactions/QRcodesForPayto.swift | 32++++++++++++++++----------------
4 files changed, 49 insertions(+), 51 deletions(-)

diff --git a/TalerCommon/HelperViews/QRGeneratorView.swift b/TalerCommon/HelperViews/QRGeneratorView.swift @@ -21,14 +21,26 @@ */ import SwiftUI +public func QRCode(from text: String) -> UIImage? { + guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil } + let data = text.data(using: .utf8, allowLossyConversion: false) + filter.setValue(data, forKey: "inputMessage") + filter.setValue("Q", forKey: "inputCorrectionLevel") // 4 levels: L M Q H + guard let ciimage = filter.outputImage else { return nil } + let transform = CGAffineTransform(scaleX: 10, y: 10) + let scaledCIImage = ciimage.transformed(by: transform) + // a UIImage(ciImage:) has no bitmap representation, so pngData() would return nil + guard let cgImage = CIContext().createCGImage(scaledCIImage, + from: scaledCIImage.extent) else { return nil } + return UIImage(cgImage: cgImage) +} +// MARK: - struct QRGeneratorView: View { let text: String let size: CGFloat let logo: Image? let logoSize: CGFloat - @Binding var image: UIImage? - - @State private var failed = false + let image: UIImage? // a QR code which cannot be generated must not look like an empty area to scan private var failedText: Text { @@ -49,40 +61,14 @@ struct QRGeneratorView: View { .scaledToFit() .frame(width: logoSize * 2, height: logoSize) } - } else if failed { + } else { failedText .talerFont(.title2) .frame(width: size, height: size) - } else { - EmptyView() } } .accessibilityElement(children: .combine) - .accessibilityLabel(failed ? failedText : Text("QR Code", comment: "a11y")) - .onAppear { -// if let uiImage = getQRCode(text: text) { - if let data = getQRCodeData(text: text), - let uiImage = UIImage(data: data) { - image = uiImage - } else { - failed = true - } - } - } - -// func getQRCode(text: String) -> UIImage? { - func getQRCodeData(text: String) -> Data? { - guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil } - let data = text.data(using: .utf8, allowLossyConversion: false) - filter.setValue(data, forKey: "inputMessage") - filter.setValue("Q", forKey: "inputCorrectionLevel") // 4 levels: L M Q H - guard let ciimage = filter.outputImage else { return nil } - let transform = CGAffineTransform(scaleX: 10, y: 10) - let scaledCIImage = ciimage.transformed(by: transform) - // a UIImage(ciImage:) has no bitmap representation, so pngData() would return nil - guard let cgImage = CIContext().createCGImage(scaledCIImage, from: scaledCIImage.extent) - else { return nil } - return UIImage(cgImage: cgImage).pngData() + .accessibilityLabel(image == nil ? failedText : Text("QR Code", comment: "a11y")) } } diff --git a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift @@ -82,7 +82,9 @@ struct QRCodeDetailView: View { GradientBorder(size: size + 20.0, color: WalletColors().primaryAccent, background: WalletColors().backgroundColor) - QRGeneratorView(text: talerURI, size: size, logo: logo, logoSize: logoSize, image: $qrImage) + QRGeneratorView(text: talerURI, size: size, + logo: logo, logoSize: logoSize, + image: qrImage) .frame(maxWidth: .infinity, alignment: .center) } if #available(iOS 17.7, *) { @@ -105,6 +107,9 @@ struct QRCodeDetailView: View { } .task(id: controller.currencyTicker) { await currencyTickerChanged() } .task { + if let image = QRCode(from: talerURI) { + qrImage = image + } withAnimation { if #available(iOS 17.7, *) { showQRcode = showQRauto17 diff --git a/TalerWallet1/Views/OIM/OIMp2pReadyView.swift b/TalerWallet1/Views/OIM/OIMp2pReadyView.swift @@ -27,7 +27,7 @@ struct OIMp2pReadyView: View { // let _ = Self._printChanges() OIMbackground() { - VStack { + Group { if let transactionId { if talerTX.isPendingReady { let details = talerTX.detailsToShow() @@ -35,14 +35,21 @@ struct OIMp2pReadyView: View { if keys.contains(TALERURI) { if let talerURI = details[TALERURI] { if talerURI.count > 10 { - Group { + VStack { Spacer() let size = 240.0 - QRGeneratorView(text: talerURI, size: size, logo: nil, logoSize: size / 4, image: $qrImage) + QRGeneratorView(text: talerURI, size: size, + logo: nil, logoSize: size / 4, + image: qrImage) .frame(maxWidth: .infinity, alignment: .center) .accessibilityLabel(Text("QR Code", comment: "a11y")) Spacer() } + .task { + if let image = QRCode(from: talerURI) { + qrImage = image + } + } } } } diff --git a/TalerWallet1/Views/Transactions/QRcodesForPayto.swift b/TalerWallet1/Views/Transactions/QRcodesForPayto.swift @@ -29,27 +29,27 @@ struct QRcodeCopyShare: View { } var body: some View { -// Text(spec.type) + let textToShare = textToShare() if let qrCodes = option.qrCodes { ForEach(qrCodes, id: \.self) { qrCodeSpec in let logo = qrCodeSpec.type == "spc" ? Image(SWISS_QR) : nil let size = 276.0 // 46x46mm, swiss flag = 7x7mm plus border 276*9/46 = 54 - QRGeneratorView(text: qrCodeSpec.qrContent, size: size, - logo: logo, logoSize: size * 9 / 46, - image: $qrImage) - .frame(maxWidth: .infinity, alignment: .center) - .listRowSeparator(.hidden) - } - } - if let textToShare = textToShare() { - HStack { -// Text(verbatim: "|") // only reason for this leading-aligned text is to get a nice full length listRowSeparator -// .accessibilityHidden(true) -// .foregroundColor(Color.clear) -// Spacer() - CopyShare(stack.push(), textToShare, image: qrImage) - .disabled(false) + let image = QRCode(from: qrCodeSpec.qrContent) + if let image { + QRGeneratorView(text: qrCodeSpec.qrContent, size: size, + logo: logo, logoSize: size * 9 / 46, + image: image) + .frame(maxWidth: .infinity, alignment: .center) + .listRowSeparator(.hidden) + } + if let textToShare { + CopyShare(stack.push(), textToShare, image: image) + .disabled(false) + } } + } else if let textToShare { + CopyShare(stack.push(), textToShare) + .disabled(false) } } }