7

我正在创建一个使用 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时,在返回视图时,将从先前的扫描中重复调用委托。然后这会导致视图层次结构中的另一个损坏。

总结一下:委托方法被快速连续调用多次,或者在返回视图时使用(旧)扫描调用委托。任何帮助,将不胜感激。

编辑:我已经部分设法解决了与委托有些坐立不安的问题,但我仍然有多次调用委托方法的问题。如果您在不到五秒的时间内从下一个视图控制器返回,则将再次调用委托方法。

4

5 回答 5

7

我认为您已经使用 captureSession?.startRunning() 方法启动了 captureSession,但是一旦您从委托中的 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)
        }
    }
}
于 2016-05-06T07:45:13.343 回答
3

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;
}
于 2015-03-26T08:13:55.020 回答
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];
}
于 2014-10-30T07:17:04.150 回答
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];
于 2014-09-12T17:07:33.117 回答
0

解决方法是向委托类添加一个布尔属性,该属性在第一次识别捕获条形码事件后切换为 false。

该解决方案按照Calin Chitu提供的方式实施。

您还需要使用 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
     }

  }
于 2014-08-27T08:11:01.200 回答