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

iOS Swift:VisionKit文档扫描框架如何将图片传递至示例项目?

解决VisionKit扫描框架无法向示例项目传递图片的问题

我来帮你搞定这个问题!你的核心问题是completion handler的触发时机完全错误——在imageFromFile方法里,你刚弹出扫描控制器就立刻检查imageNew,这时候用户还没开始扫描,imageNew必然是nil,导致completion直接返回了空值,根本没等到扫描完成后的结果。

问题代码分析

  1. 你的imageFromFile方法中,present相机控制器后马上执行if imageNew != nil判断,这时候扫描流程还没启动,所以直接调用result(nil)结束了completion。
  2. 每次调用A8Scan(self).imageFromFile都会创建新的A8Scan实例,扫描完成后原实例已被释放,completion自然无法触发。
  3. 没有在扫描完成、失败或取消的回调中正确触发completion,导致结果无法传递到示例项目。

修改后的框架代码(A8Scan类)

public class A8Scan: NSObject, VNDocumentCameraViewControllerDelegate {
    var imageNew: UIImage?
    var statusImage: UIImageView?
    private var clientView: UIViewController?
    // 新增:用于保存外部传入的completion handler
    private var imageCompletion: ((UIImage?) -> Void)?
    
    public init(_ viewController:UIViewController){
        self.clientView = viewController
    }
    
    public func showScanner(imgData: UIImage?){
        self.createTaskController(img: imgData)
        print("Called Build")
    }
    
    private func createTaskController(img: UIImage?){
        let scannerViewController = VNDocumentCameraViewController()
        scannerViewController.delegate = self
        self.clientView?.present(scannerViewController,animated:true,completion: {
            self.imageNew = img
        })
    }
    
    // 重写后的imageFromFile方法:只保存completion并弹出扫描器
    public func imageFromFile(completion: @escaping (_ image: UIImage?) -> Void){
        self.imageCompletion = completion
        let scannerViewController = VNDocumentCameraViewController()
        scannerViewController.delegate = self
        self.clientView?.present(scannerViewController,animated:true,completion: nil)
    }
    
    func set(image: UIImage) {
        self.statusImage?.image = image
    }
    
    public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
        guard scan.pageCount >= 1 else {
            controller.dismiss(animated: true) {
                // 无扫描页面时返回nil
                self.imageCompletion?(nil)
                self.imageCompletion = nil
            }
            return
        }
        let originalImage = scan.imageOfPage(at: 0)
        let newImage = compressedImage(originalImage)
        imageNew = newImage
        set(image: imageNew!)
        print("new image::\(newImage.size)")
        
        // 相机关闭后再触发completion传递图片
        controller.dismiss(animated: true) {
            self.imageCompletion?(newImage)
            // 清空completion避免内存泄漏
            self.imageCompletion = nil
        }
    }
    
    public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
        print(error)
        controller.dismiss(animated: true) {
            self.imageCompletion?(nil)
            self.imageCompletion = nil
        }
    }
    
    public func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
        controller.dismiss(animated: true) {
            // 用户取消扫描时返回nil
            self.imageCompletion?(nil)
            self.imageCompletion = nil
        }
    }
    
    func compressedImage(_ originalImage: UIImage) -> UIImage {
        guard let imageData = originalImage.jpegData(compressionQuality: 1), let reloadedImage = UIImage(data: imageData) else {
            return originalImage
        }
        return reloadedImage
    }
}

修改后的示例项目调用代码

// 在你的ViewController类中添加属性,复用同一个A8Scan实例
private let scanner = A8Scan(self)

@IBAction func btnAction(_ sender: Any) {
    scanner.imageFromFile { [weak self] image in
        guard let self = self else { return }
        if let scannedImage = image {
            print("成功获取扫描图片:\(scannedImage.size)")
            // 这里可以将图片赋值给你的UIImageView或进行其他处理
        } else {
            print("扫描失败或用户取消操作")
        }
    }
}

关键修改点说明

  1. 保存completion handler:新增imageCompletion属性,把外部传入的completion暂存起来,等待扫描流程结束后再触发。
  2. 延迟触发completion:在扫描完成、失败、取消的回调中,等到相机控制器关闭后再调用completion,确保结果能正确传递。
  3. 复用A8Scan实例:将A8Scan作为ViewController的属性,避免每次调用都新建实例导致扫描完成后原对象被销毁。
  4. 内存安全:使用[weak self]避免循环引用,调用完completion后清空imageCompletion防止内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:01:38