即使在返回视图后,也会多次调用Delegate方法

时间:2013-09-30 11:26:26

标签: ios objective-c delegates

我正在创建一个在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方法。

5 个答案:

答案 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];