layer.removeFromSuperlayer

ios

#1

Calling layer.removeFromSuperlayer() does not remove layer.

I’ve implemented a QRCode scanner. Starting and stopping the session works, and it’s successful in reading the barcodes. But I can’t remove the layer, AVCaptureVideoPreviewLayer.

I wont to remove the layer from the callback captureOutputDidOutputMetadataObjectsFromConnection, as soon as it detects and reads the barcode. This is where I’m calling session.stopRunning(), successfully.

I’ve also tried removing from a time-out, but nothing.

No error is being thrown.


#2

I guess you are playing with native apis, is it possible for you to share the full example?


#3
const video = AVMediaTypeVideo;
const CaptureDevice = AVCaptureDevice;
const authorized = CaptureDevice.authorizationStatusForMediaType(video);

class QRCodeScanner {
  constructor(view) {
    this.view = view.ios;
    this.Device = AVCaptureDevice;
    this.Session = AVCaptureSession.alloc().init();
    this.preview_layer = AVCaptureVideoPreviewLayer.layerWithSession(
      this.Session
    );
  }

  delegate() {
    const session = this.Session;
    const layer = this.preview_layer;
    const MetadataOutputDelegate = AVCaptureMetadataOutput.extend(
      {
        captureOutputDidOutputMetadataObjectsFromConnection: function(
          output,
          objects,
          connection
        ) {
          session.stopRunning();
          layer.removeFromSuperlayer();
          console.log(objects[0].stringValue);
        }
      },
      {
        name: "MetadataOutputDelegate",
        protocols: [AVCaptureMetadataOutputObjectsDelegate]
      }
    );

    return new MetadataOutputDelegate();
  }

  scan() {
    switch (authorized) {
      case AVAuthorizationStatus.Authorized:
        this.initiatCaptureSession();
        break;
      case AVAuthorizationStatus.NotDetermined:
        this.requestPermission();
        break;
      case AVAuthorizationStatus.Denied:
        alert("Access has been Denied!");
        break;
    }
  }

  initiatCaptureSession() {
    const delegate = this.delegate();
    const dispatchQueue = dispatch_queue_create(null, null);
    const video_device = this.Device.defaultDeviceWithMediaType(video);
    const video_input = AVCaptureDeviceInput.deviceInputWithDeviceError(
      video_device,
      null
    );

    const MetadataOutput = AVCaptureMetadataOutput.alloc().init();

    this.Session.beginConfiguration();
    this.Session.canAddInput(video_input);
    this.Session.addInput(video_input);
    this.Session.canAddOutput(MetadataOutput);
    this.Session.addOutput(MetadataOutput);
    this.Session.commitConfiguration();

    MetadataOutput.setMetadataObjectsDelegateQueue(delegate, dispatchQueue);
    MetadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode];

    this.preview_layer.frame = this.view.layer.bounds;
    this.view.layer.addSublayer(this.preview_layer);

    this.Session.startRunning();
  }

  requestPermission() {
    this.Device.requestAccessForMediaTypeCompletionHandler(
      video,
      granted => (granted ? this.initiatCaptureSession() : null)
    );
  }
}

Also, happy birthday :tada:


#4

Thank you for the wishes :slight_smile:

May I ask why don’t you extend this component directly from View so it inherits all the View capabilities? Also would you mind sharing a working example in Playground so it helps to debug.


#5

#6

Taking stopRunning() and removeFromSuperlayer() out of the delegate and calling them from a setTimeout() raised just after startRunning() works. But obviously not the solution.


#7

You must follow the NativeScript’s View architecture to prevent such issues completely.

Refer the docs here, it need not to be a plugin. In the same JS file itself you can write the code. Use createNativeView and disposeNativeView callbacks to create and cleanup native views / session / delegates etc.,
https://docs.nativescript.org/plugins/ui-plugin-custom