You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift Vision识别文本无法赋值给SwiftUI结构体属性用于显示

问题:SwiftUI中Vision文本识别回调无法更新@State变量

问题描述

使用Vision库识别图片文本时,回调函数recognizeTextHandler里无法将识别结果赋值给@State变量recognizedText。已确认文本识别请求执行成功(返回"Text recognition succeeded."),但recognizedText始终保持初始值。

简化代码

struct AnnotatorView: View {
    @State private var image: UIImage?
    @State private var recognizedText: String = "No text recognized."
    @State private var isAnnotatedImageDisplayed = false // 补充缺失的状态变量

    func recognizeTextHandler(request: VNRequest, error: Error?) -> Void {
        guard let results = request.results as? [VNRecognizedTextObservation] else { return }

        let recognizedStrings: Array<String> = results.compactMap({ result in
            result.topCandidates(1).first!.string
        })

        // 无法更新的代码
        self.recognizedText = recognizedStrings.joined()
    }

    func performTextRecognition(requestHandler: VNImageRequestHandler, request: VNRecognizeTextRequest) -> String {
        do {
            try requestHandler.perform([request])
            return "Text recognition succeeded."
        } catch {
            return "Could not perform text recognition request because of the following error: \(error)"
        }
    }

    var body: some View {
        VStack(spacing: 15){
            // 相机/图片选择按钮逻辑省略

            .fullScreenCover(isPresented: self.$isAnnotatedImageDisplayed) {
                VStack {
                    // 关闭按钮逻辑省略

                    if let cgImage: CGImage = self.image?.cgImage {
                        let requestHandler: VNImageRequestHandler = VNImageRequestHandler(cgImage: cgImage)
                        let recognizeTextRequest = VNRecognizeTextRequest(completionHandler: recognizeTextHandler)
                        let textRecognitionStatus: String = performTextRecognition(requestHandler: requestHandler, request: recognizeTextRequest)

                        Text(textRecognitionStatus)
                        // 图片显示逻辑省略

                        if !self.recognizedText.isEmpty {
                            Text("\(self.recognizedText)")
                        } else {
                            Text("No text recognized because the image is not good enough.")
                        }
                    } else {
                        Text("You haven't taken any pictures yet!")
                        Text("\(self.recognizedText)")
                    }
                }
            }
        }
    }
}

struct AnnotatorView_Previews: PreviewProvider {
    static var previews: some View {
        AnnotatorView()
    }
}

已尝试的排查步骤

  • 确认文本识别请求执行成功,textRecognitionStatus显示"Text recognition succeeded."
  • 尝试用数组存储识别结果,仍无法更新UI状态
  • 检查SwiftUI控制流逻辑,其他文本可正常显示

解决方案

问题核心在于两个点:

  1. 线程不匹配:Vision的默认回调在后台线程执行,而SwiftUI的@State变量必须在主线程更新才能触发UI刷新。
  2. 回调捕获问题:直接传递回调可能导致self捕获异常,需显式处理线程切换。

1. 修复回调的线程切换

更新recognizeTextHandler,确保在主线程更新状态:

func recognizeTextHandler(request: VNRequest, error: Error?) -> Void {
    guard let results = request.results as? [VNRecognizedTextObservation] else { return }

    let recognizedStrings = results.compactMap { $0.topCandidates(1).first?.string }
    let finalText = recognizedStrings.joined()

    // 切换到主线程更新@State变量
    DispatchQueue.main.async {
        self.recognizedText = finalText
    }
}

2. 优化请求配置(可选)

添加识别精度、多语言支持等配置,提升识别稳定性:

let recognizeTextRequest = VNRecognizeTextRequest(completionHandler: recognizeTextHandler)
// 优先精度(可选改为.fast追求速度)
recognizeTextRequest.recognitionLevel = .accurate
// 支持多语言(按需添加)
recognizeTextRequest.recognitionLanguages = ["en-US", "zh-Hans"]

3. 添加加载状态提示(可选)

由于识别是异步操作,UI会先渲染初始值,可添加加载状态提升用户体验:

@State private var isRecognizing = false

// 更新performTextRecognition方法
func performTextRecognition(requestHandler: VNImageRequestHandler, request: VNRecognizeTextRequest) -> String {
    DispatchQueue.main.async {
        self.isRecognizing = true
    }
    do {
        try requestHandler.perform([request])
        return "Text recognition succeeded."
    } catch {
        DispatchQueue.main.async {
            self.isRecognizing = false
        }
        return "Could not perform text recognition request because of the following error: \(error)"
    }
}

// 在回调中结束加载状态
func recognizeTextHandler(request: VNRequest, error: Error?) -> Void {
    // ... 原有识别逻辑 ...
    DispatchQueue.main.async {
        self.recognizedText = finalText
        self.isRecognizing = false
    }
}

// UI中替换原文本显示逻辑
if isRecognizing {
    ProgressView("Recognizing text...")
} else {
    if !self.recognizedText.isEmpty {
        Text("\(self.recognizedText)")
    } else {
        Text("No text recognized because the image is not good enough.")
    }
}

关键原理

  • SwiftUI的@State变量绑定的UI刷新仅在主线程生效,后台线程的赋值不会触发视图更新。
  • Vision框架的请求执行是异步的,回调会在系统分配的后台线程调用,必须手动切换到主线程更新状态。

内容的提问来源于stack exchange,提问作者Jaiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:40:28