我正在创建一个在iOS 7中使用新条形码扫描程序的应用程序,但我遇到了委托方法的一些问题。扫描程序正确识别条形码并调用委托方法,但它执行速度太快,因此调用连续多次发生,导致segue被执行多次。代理方法如下。
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
connection.enabled = NO;
self.conn = connection;
for (AVMetadataObject *metadata in metadataObjects) {
if ([metadata.type isEqualToString:AVMetadataObjectTypeEAN8Code] || [metadata.type isEqualToString:AVMetadataObjectTypeEAN13Code]) {
self.strValue = [(AVMetadataMachineReadableCodeObject *)metadata stringValue];
NSLog(@"%@", [(AVMetadataMachineReadableCodeObject *)metadata corners]);
}
}
[self performSegueWithIdentifier:@"newSegue" sender:self];
}
问题在于,如果我没有在开始行中设置connection.enabled = NO
,则会多次调用委托,从而导致损坏的视图层次结构(然后崩溃)。另一个问题是,当我在viewWillAppear中使用self.conn = YES
禁用连接然后重新启用连接时,将在返回视图时从先前扫描重复调用该委托。这会导致视图层次结构中出现另一个损坏。
总而言之:快速连续多次调用委托方法,或者在返回视图时使用(旧)扫描调用委托。任何帮助将不胜感激。
编辑:我已经部分设法解决了与委托一些烦躁的问题,但我仍然有一个问题,委托方法被多次调用。如果你在不到五秒的时间内从下一个viewcontroller返回,那么将再次调用delegate方法。
答案 0 :(得分:6)
我认为你已经开始使用captureSession了 captureSession?.startRunning()方法但是一旦从委托中的QRCode输出就没有停止...
只需使用此[captureSession stopRunning]; //在Objective-C
中 下面是我在swift中为同样的问题所做的事情// MARK: - AVCapture委托在检测到时找到元数据
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRectZero
return
}
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if metadataObj.type == AVMetadataObjectTypeQRCode {
// If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObjectForMetadataObject(metadataObj as AVMetadataMachineReadableCodeObject) as! AVMetadataMachineReadableCodeObject
qrCodeFrameView?.frame = barCodeObject.bounds;
if metadataObj.stringValue != nil {
captureSession?.stopRunning() // Stop captureSession here... :)
self.performSegueWithIdentifier("yourNextViewController", sender: self)
}
}
}
答案 1 :(得分:2)
我希望,这会节省其他时间。 这是文章,如何使用条形码扫描仪 http://www.appcoda.com/qr-code-ios-programming-tutorial/
从Apple文档:"此方法可能会被频繁调用,您的实现应该有效地防止捕获性能问题,包括丢弃的元数据对象。"
现在,要处理多次调用,请执行以下操作:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
id capturedData;
if ([metadataObjects count] > 0) {
// handle your captured data here
[self performSelectorOnMainThread:@selector(stopReading:) withObject:capturedData waitUntilDone:NO];
}
}
stopReading:方法看起来(假设您的_session是AVCaptureSession对象而_prevLayer是您之前使用过的AVCaptureVideoPreviewLayer):
-(void)stopReading:(id) data{
NSLog(@"stop reading");
[_session stopRunning];
_session = nil;
[_prevLayer removeFromSuperlayer];
// do what you want with captured data
[self.delegate didScanBarCodeWithContext:data];
}
答案 2 :(得分:2)
Doro的答案很好,但有bug:在第二次调用委托方法之前,可能无法及时调用函数'stopReading'
所以我做了一些优化。
根据Doro的回答,我添加了一个静态变量来告诉他们。
static BOOL hasOutput;
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
if (!hasOutput
&& metadataObjects.count > 0 ) {
hasOutput=YES;
[self performSelectorOnMainThread:@selector(stopReading) withObject:nil waitUntilDone:NO];
for (AVMetadataObject *current in metadataObjects) {
if ([current isKindOfClass:[AVMetadataMachineReadableCodeObject class]]
&& [_metadataObjectTypes containsObject:current.type]) {
NSString *scannedResult = [(AVMetadataMachineReadableCodeObject *) current stringValue];
if (_completionBlock) {
_completionBlock(scannedResult);
}
break;
}
}
}
}
-(void)stopReading{
NSLog(@"stop reading");
[_session stopRunning];
_session = nil;
hasOutput=NO;
}
答案 3 :(得分:0)
解决方法是在委托类中添加一个布尔属性,该属性在第一次识别捕获条形码事件后切换为false。
此解决方案以[{3}}提供的方式实施。
您还需要使用YES初始化属性shouldSendReadBarcodeToDelegate一次。
@property (nonatomic, assign) BOOL shouldSendReadBarcodeToDelegate;
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
if (!self.shouldSendReadBarcodeToDelegate)
{
//this means we have already captured at least one event, then we don't want to call the delegate again
return;
}
else
{
self.shouldSendReadBarcodeToDelegate = NO;
//Your code for calling the delegate should be here
}
}
答案 4 :(得分:0)
布尔属性对我来说不起作用。我已经使用操作队列结束以避免多次读取:
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection
{
if ([self.queue operationCount] > 0) return;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
if ([metadataObjects count] > 0) {
// Your code here: Don't forget that you are in background now, perform
// all view related stuff on main thread
}
}];
[self.queue addOperations:@[operation] waitUntilFinished:NO];
}
在viewcontroller构造函数中初始化队列:
self.queue = [NSOperationQueue new];