iOS 17.4.1 Widget二维码无法渲染问题求助
搭载iOS 17.4.1的iPhone 12设备上,Widget中的二维码无法正常显示,仅展示App logo,Widget其余内容显示正常。该功能在iOS 17及更早版本中运行完全正常,升级至iOS 17.4.1后仅Widget内的二维码渲染失效,App内的二维码生成功能无异常。使用Xcode 15.3的iOS 17.4模拟器测试时,Widget二维码功能正常。
询问:是否有其他开发者遇到iOS 17.4.1 Widget的该问题?是否存在已知的解决方法或修复方案?
QRCodeSmallWidgetView
import SwiftUI import WidgetKit struct QRCodeSmallWidgetView: View { // Create the UserDefaults suites for Widget let appWidgetSuite = UserDefaults(suiteName: "group.zxc.asd.app.Abc-Widget") var body: some View { ZStack { // widget content if let userURL = appWidgetSuite?.string(forKey: "UserWidgetQRCode") { let imgQR = UIImage().generateQRCodeFromString(barcode: userURL) let qrImage = UIImage().convert(imgQR) let qrImageWithAppLogo = UIImage().addAppLogoToQRCodeInWidget(qrCodeImage: qrImage, logoImage: UIImage(named: "ic_qr_logo")) // Here Pass qrImage | qrImageWithAppLogo Image(uiImage: qrImageWithAppLogo) .resizable() .aspectRatio(contentMode: .fit) } else { Text("Add APP QR") } } .widgetBackground(Color(UIColor.systemBackground)) } } struct QRCodeSmallWidgetView_Previews: PreviewProvider { static var previews: some View { QRCodeSmallWidgetView() .previewContext(WidgetPreviewContext(family: .systemSmall)) } } extension View { func widgetBackground(_ backgroundView: some View) -> some View { if #available(iOSApplicationExtension 17.0, *) { return containerBackground(for: .widget) { backgroundView } } else { return background(backgroundView) } } }
QRCodeWidgetEntryView
import SwiftUI import WidgetKit struct QRCodeWidgetEntryView: View { var entry: Provider.Entry @Environment(\.widgetFamily) var family @ViewBuilder var body: some View { switch family { case .systemSmall: QRCodeSmallWidgetView() default: fatalError() } } }
App_WidgetBundle
import WidgetKit import SwiftUI @main struct App_WidgetBundle: Widget { let kind: String = "App_WidgetBundle" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in QRCodeWidgetEntryView(entry: entry) } .configurationDisplayName("App QRCode Widget") .description("Instantly share your QRCode from your home screen.") .supportedFamilies([.systemSmall]) } }
Provider
import WidgetKit import SwiftUI struct Provider: TimelineProvider { typealias Entry = SimpleEntry func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), qrImage: "staticQRCode") } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), qrImage: "staticQRCode") completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { let entry = SimpleEntry(date: Date(), qrImage: "") let timeline = Timeline(entries: [entry], policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { let date: Date let qrImage: String }
UIImage 扩展
extension UIImage { func generateQRCodeFromString(barcode: String) -> CIImage { guard let dataString = barcode.data(using: .ascii) else { fatalError("Failed to convert string to data.") } guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { fatalError("Failed to create CIFilter.") } qrFilter.setValue(dataString, forKey: "inputMessage") qrFilter.setValue("Q", forKey: "inputCorrectionLevel") guard let outputImage = qrFilter.outputImage else { fatalError("Failed to generate output image.") } let transform = CGAffineTransform(scaleX: 5.0, y: 5.0) return outputImage.transformed(by: transform) } func convert(_ cmage: CIImage) -> UIImage { let context: CIContext = CIContext.init(options: nil) let cgImage: CGImage = context.createCGImage(cmage, from: cmage.extent)! let image: UIImage = UIImage.init(cgImage: cgImage) return image } func addAppLogoToQRCodeInWidget(qrCodeImage: UIImage, logoImage: UIImage?) -> UIImage { let size = CGSize(width: qrCodeImage.size.width, height: qrCodeImage.size.height) UIGraphicsBeginImageContextWithOptions(size, false, 0.0) qrCodeImage.draw(in: CGRect(origin: CGPoint.zero, size: size)) // Calculate the position to place the logo in the center let logoSize = CGSize(width: size.width * 0.48, height: size.height * 0.22) let origin = CGPoint(x: (size.width - logoSize.width) / 2, y: (size.height - logoSize.height) / 2) logoImage?.draw(in: CGRect(origin: origin, size: logoSize)) let finalImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return finalImage } }
将二维码生成逻辑移至TimelineProvider
Widget的body渲染线程有严格的性能限制,iOS 17.4.1可能收紧了同步图形操作的限制。建议在Provider的getTimeline方法中提前生成二维码,将生成后的图片数据(如Base64编码)存入App Group的UserDefaults,或直接传递到SimpleEntry中,在View中直接加载预生成的图片。替换图片合成方式
避免使用UIGraphicsBeginImageContextWithOptions在Widget中合成图片,改用SwiftUI的视图叠加方式实现logo添加:ZStack { Image(uiImage: qrImage) .resizable() .aspectRatio(contentMode: .fit) Image("ic_qr_logo") .resizable() .frame(width: qrImage.size.width * 0.48, height: qrImage.size.height * 0.22) }使用第三方QR生成库
替换Core Image的CIQRCodeGenerator,使用纯Swift的QR生成库(如QRCode.swift),避免系统版本带来的滤镜兼容性问题。检查资源与权限
- 确认
ic_qr_logo图片已添加至Widget target的资源文件中 - 验证App Group配置正确,Widget可正常读写
UserDefaults中的UserWidgetQRCode值
- 确认
异步生成与Timeline刷新
在Widget中通过Task异步生成二维码,生成完成后调用WidgetCenter.shared.reloadAllTimelines()刷新Widget,但需注意Widget中异步操作的时间限制。
内容的提问来源于stack exchange,提问作者Mayur Mori

