iOS:相机在第一次加载视图控制器时无法识别二维码

iOS: Camera does not recognize QR Code on first load of view controller

我有一个小应用程序,它可以读取二维码进行登录,也可以提供手动输入代码和登录的可能性。 该应用程序启动并直接前往登录(查看)。当我尝试扫描不起作用的二维码时 - 代表永远不会 called/the 事件从未引发。

我采用了 Larry OBrien 的方法 http://www.knowing.net/index.php/2013/10/09/natively-recognize-barcodesqr-codes-in-ios-7-with-xamarin-ios/

并为此用途创建了我自己的 ScannerView class:

public sealed partial class ScannerView : UIView
{
    private readonly AVCaptureVideoPreviewLayer _layer;
    public AVCaptureSession Session { get; }
    private readonly AVCaptureMetadataOutput _metadataOutput;

    public event EventHandler<AVMetadataMachineReadableCodeObject> MetadataFound = delegate { };
    public ScannerView (IntPtr handle) : base (handle)
    {
        Session = new AVCaptureSession();
        var camera = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
        var input = AVCaptureDeviceInput.FromDevice(camera);
        Session.AddInput(input);

        //Add the metadata output channel
        _metadataOutput = new AVCaptureMetadataOutput {RectOfInterest = Bounds};
        var metadataDelegate = new MetadataOutputDelegate();
        var dispatchQueue = new DispatchQueue("scannerQueue");
        _metadataOutput.SetDelegate(metadataDelegate, dispatchQueue);
        Session.AddOutput(_metadataOutput);

        _layer = new AVCaptureVideoPreviewLayer(Session)
        {
            MasksToBounds = true,
            VideoGravity = AVLayerVideoGravity.ResizeAspectFill,
            Frame = Bounds
        };

        Layer.AddSublayer(_layer);

        // Hand event over to subscriber
        metadataDelegate.MetadataFound += (s, e) => MetadataFound(s, e);
    }

    public override void LayoutSubviews()
    {
        base.LayoutSubviews();
        _layer.Frame = Bounds;
        _metadataOutput.RectOfInterest = Bounds;
    }

    public void SetMetadataType(AVMetadataObjectType type)
    {
        //Confusing! *After* adding to session, tell output what to recognize...
        _metadataOutput.MetadataObjectTypes = type;
    }
}

在我的登录视图中,我执行以下操作:

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        // Manipulate navigation stack
        NavigationController.SetViewControllers(
            NavigationController.ViewControllers.Where(
                viewController => viewController is LoginView).ToArray(), false);

        ScannerView.MetadataFound += (s, e) =>
        {
            Console.WriteLine($"Found: [{e.Type.ToString()}] {e.StringValue}");
            LoginViewModel.BarCode = e.StringValue;
            if (LoginViewModel.DoneCommand.CanExecute())
            {
                ScannerView.Session.StopRunning();
                LoginViewModel.DoneCommand.Execute();
            }
        };
    }

    public override void ViewDidAppear(bool animated)
    {
        base.ViewDidAppear(animated);
        ScannerView.Session.StartRunning();
        ScannerView.SetMetadataType(AVMetadataObjectType.QRCode | AVMetadataObjectType.EAN13Code);
    }

有趣的是,一旦我使用手动输入登录并再次注销,这就起作用了,所以我再次出现在同一个屏幕上(可能不一样但是它的一个新实例,因为 GC 可能会破坏从导航堆栈中删除的视图?)

我已将 scannerview 作为子视图放在情节提要中的 LoginView 上。对于导航,我使用 MVVMCross。 (仅供参考)

所以:我做错了什么?我需要做什么才能让它在第一次加载时工作? (我让它做了一次 - 使用相同的代码......也许这是一个时间问题?)

显然这是一个时间问题。 我通过添加 "Tap to scan" 范例来解决它。 点击时我执行以下代码:

        public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);
        Console.WriteLine($"Current types to scan: {this.MetadataOutput.MetadataObjectTypes}");
        this.SetMetadataType(this.MetadataObjectType);
        Console.WriteLine($"New types to scan: {this.MetadataOutput.MetadataObjectTypes}");
    }

    public void SetMetadataType(AVMetadataObjectType type)
    {
        //Confusing! *After* adding to session, tell output what to recognize...
        this.Session.BeginConfiguration();
        this.MetadataOutput.MetadataObjectTypes = type;
        this.Session.CommitConfiguration();
    }

其中 MetadataObjectType 设置为我们之前要查找的代码。 这解决了问题 - 扫描现在每次都有效。 我认为神奇的部分是 Begin- 和 CommitConfiguration 调用,因为如果我不使用触摸扫描范例,这也有效。