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 调用,因为如果我不使用触摸扫描范例,这也有效。
我有一个小应用程序,它可以读取二维码进行登录,也可以提供手动输入代码和登录的可能性。 该应用程序启动并直接前往登录(查看)。当我尝试扫描不起作用的二维码时 - 代表永远不会 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 调用,因为如果我不使用触摸扫描范例,这也有效。