I’ve been utilizing Zxing.Maui nuget for QR code scanning nevertheless it doesn’t work for some small QR codes. After some digging I discovered that AVCaptureSession
with IAVCaptureMetadataOutputObjectsDelegate
works for this. I’m referring vinodiOS/SwiftQRCodeScanner to realize this.
My MAUI customized management code as follows. In debug I do see view is being created and CaptureSession operating nevertheless UI doesn’t present digital camera preview. I additionally tried so as to add this ZXing.MAUI fork however IAVCaptureMetadataOutputObjectsDelegate.DidOutputMetadataObjects doesn’t get hit there even with [Export("captureOutput:didOutputMetadataObjects:fromConnection:")]
getting used. I seemed up how they’re including sublayering VideoPreviewLayer in Zxing.MAUI library and tried that right here as nicely(see LayoutSubviews()
beneath). That additionally doesn’t present UI.
I’m doing one thing incorrect clearly. Cant determine what.
public class QRCodeReaderView : View
{
public static readonly BindableProperty IsScanningProperty = BindableProperty.Create(nameof(IsScanning), typeof(bool), typeof(CameraView), true);
public bool IsScanning
{
get => (bool)GetValue(IsScanningProperty);
set => SetValue(IsScanningProperty, worth);
}
public occasion EventHandler? OnBarcodeDetected;
public void RaiseBarcodeDetected(string barcode)
{
OnBarcodeDetected?.Invoke(this, new BarcodeDetectedEventArgs(barcode));
}
}
public partial class QRCodeReaderViewHandler : ViewHandler
{
protected override IosQRCodeReaderView CreatePlatformView()
{
var management = new IosQRCodeReaderView();
return management;
}
personal static void MapIsScanning(QRCodeReaderViewHandler handler, QRCodeReaderView qrCodeReaderView)
{
}
personal static void MapOnBarcodeDetected(QRCodeReaderViewHandler handler, QRCodeReaderView qrCodeReaderView, object? args)
{
//TODO
}
}
Native view
utilizing System.Diagnostics;
utilizing AVFoundation;
utilizing CoreAnimation;
utilizing CoreFoundation;
utilizing Basis;
utilizing UIKit;
namespace QRCodeScanner;
public class IosQRCodeReaderView : UIView, IAVCaptureMetadataOutputObjectsDelegate
{
personal readonly AVCaptureDevice? _defaultDevice;
personal AVCaptureInput? _defaultCaptureInput => _defaultDevice just isn't null ? AVCaptureDeviceInput.FromDevice(_defaultDevice) : null;
personal readonly AVCaptureMetadataOutput _dataOutput;
personal readonly AVCaptureSession _captureSession;
personal readonly AVCaptureDevicePosition _devicePosition = AVCaptureDevicePosition.Again;
public occasion EventHandler? QRCodeDetected;
personal readonly AVCaptureVideoPreviewLayer _videoPreviewLayer;
public IosQRCodeReaderView(AVCaptureDevicePosition place = AVCaptureDevicePosition.Again)
{
_defaultDevice = AVCaptureDevice.GetDefaultDevice(AVMediaTypes.Video);
_captureSession = new AVCaptureSession();
_devicePosition = place;
_dataOutput = new AVCaptureMetadataOutput();
_videoPreviewLayer = new AVCaptureVideoPreviewLayer(_captureSession)
{
VideoGravity = AVLayerVideoGravity.ResizeAspectFill,
CornerRadius = 10.0f,
};
PrepareQRScanner();
StartScanningQRCode();
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
var remodel = CATransform3D.MakeRotation(0, 0, 0, 1.0f);
swap (UIDevice.CurrentDevice.Orientation)
{
case UIDeviceOrientation.Portrait:
remodel = CATransform3D.MakeRotation(0, 0, 0, 1.0f);
break;
case UIDeviceOrientation.PortraitUpsideDown:
remodel = CATransform3D.MakeRotation((nfloat)Math.PI, 0, 0, 1.0f);
break;
case UIDeviceOrientation.LandscapeLeft:
remodel = CATransform3D.MakeRotation((nfloat)(-Math.PI / 2), 0, 0, 1.0f);
break;
case UIDeviceOrientation.LandscapeRight:
remodel = CATransform3D.MakeRotation((nfloat)Math.PI / 2, 0, 0, 1.0f);
break;
case UIDeviceOrientation.Unknown:
case UIDeviceOrientation.FaceUp:
case UIDeviceOrientation.FaceDown:
default:
break;
}
_videoPreviewLayer.Rework = remodel;
_videoPreviewLayer.Body = Layer.Bounds;
AddMaskToVideoPreviewLayer();
}
personal void SetupCaptureSession()
{
if (_captureSession.Working)
{
return;
}
_captureSession.BeginConfiguration();
swap (_devicePosition)
{
case AVCaptureDevicePosition.Entrance:
//TODO
break;
case AVCaptureDevicePosition.Unspecified:
case AVCaptureDevicePosition.Again:
if (_defaultCaptureInput just isn't null && _captureSession.CanAddInput(_defaultCaptureInput))
{
_captureSession.AddInput(_defaultCaptureInput);
}
break;
default:
Debug.WriteLine("Do nothing");
break;
}
if (_captureSession.CanAddOutput(_dataOutput) is fake)
{
return;
}
_captureSession.AddOutput(_dataOutput);
_dataOutput.MetadataObjectTypes = _dataOutput.AvailableMetadataObjectTypes;
_dataOutput.SetDelegate(this, DispatchQueue.MainQueue);
Debug.WriteLine(_dataOutput.AvailableMetadataObjectTypes);
_captureSession.CommitConfiguration();
}
personal void PrepareQRScanner()
{
SetupCaptureSession();
}
personal AVCaptureDeviceInput? GetCurrentInput()
{
if (_captureSession.Inputs.FirstOrDefault() is AVCaptureDeviceInput deviceInput)
{
return deviceInput;
}
return null;
}
personal void StartScanningQRCode()
{
if (_captureSession.Working)
{
return;
}
//DispatchQueue.GetGlobalQueue(DispatchQualityOfService.Background).DispatchAsync(_captureSession.StartRunning);
MainThread.BeginInvokeOnMainThread(_captureSession.StartRunning);
}
personal void AddMaskToVideoPreviewLayer()
{
//TODO: add
}
personal int _delayCount = 0;
personal const int MaxDelayCount = 15;
[Export("captureOutput:didOutputMetadataObjects:fromConnection:")]
public void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
{
attempt
{
foreach (var knowledge in metadataObjects)
_videoPreviewLayer.Bounds.Accommodates(remodeled.Bounds) is fake)
proceed;
_delayCount += 1;
if (_delayCount < MaxDelayCount)
proceed;
if (string.IsNullOrEmpty(remodeled.StringValue))
proceed;
QRCodeDetected?.Invoke(this, new QRCodeEventArgs(remodeled.StringValue));
_captureSession.StopRunning();
}
catch (Exception ex)
{
Debug.WriteLine(ex.StackTrace);
}
}
}
Handler project
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler();
});
personal async Process CheckAndRequestCameraPermission()
{
var standing = await Permissions.CheckStatusAsync();
if (standing != PermissionStatus.Granted)
{
standing = await Permissions.RequestAsync();
if (standing != PermissionStatus.Granted)
{
await DisplayAlert("Digicam Permission Denied", "This app wants entry to the digital camera to scan QR codes.", "OK");
return; // Or disable the scanning performance
}
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
await CheckAndRequestCameraPermission();
}
If anybody has an concept why is that this occurring that will be an ideal assist.