即使在返回视图之后,委托方法也会被多次调用
我正在创build一个在iOS 7中使用新的条形码扫描器的应用程序,但是我在委托方法中遇到了一些问题。 扫描器能够正确识别条形码并调用委托方法,但是速度太快,所以调用会连续多次,导致多次执行。 下面的委托方法。
- (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
重新启用连接时,委托将在返回到视图时从先前的扫描中重复调用。 这会导致视图层次结构中的另一个损坏。
所以总结一下:无论是快速连续多次调用委托方法,还是在返回视图时都使用(旧)扫描调用委托。 任何帮助,将不胜感激。
编辑:我已经部分设法解决了与委托的一些烦躁的问题,但我仍然有多次调用委托方法的问题。 如果你在五秒内从下一个视图控制器返回,委托方法将被再次调用。
我认为你已经开始captureSession使用captureSession?.startRunning()方法,但没有停止它,一旦你从委托的QRCode输出…
只要使用这个[captureSession stopRunning]; //在Objective-C中
下面是我为迅速做同样的事情而做的
// 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) } } }
Doro的答案是好的,但有bug:函数“stopReading”可能不会及时调用委托方法第二次
所以我做一些优化。
根据Doro的回答,我添加了一个Staticvariables来告诉他们。
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; }
我希望这会节省时间。 这里是文章,如何使用条码扫描器http://www.appcoda.com/qr-code-ios-programming-tutorial/
从苹果文档:“这个方法可能会被频繁调用,你的实现应该是有效的,以防止捕获性能问题,包括丢弃的元数据对象。
现在,要处理多个调用,请执行以下操作:
- (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]; }
解决方法是向委托类添加一个布尔属性,该属性在首次识别捕获条形码事件后切换为false。
这个解决scheme是以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 } }
布尔属性不为我做的伎俩。 我已经结束使用操作队列,以避免多个读取:
- (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];