アイキャッチ画像

【iOS】.NET MAUIでZXingのカメラが停止しない時の対処方法

.NET MAUIでバーコードリーダーを実装するためにZXingを利用することがあると思います。

しかし、iOSで利用するとカメラが停止しないことがあります。当記事では、その対処方法を紹介していきます。

Android・Windowsでは、再表示にカメラが動作しない問題があります。問題の詳細は以下で紹介していますので確認してみてください。

紹介環境

当記事は以下の環境で作成しています。

開発環境

  • Visual Studio 2022
  • .Net 8

パッケージ

  • ZXing.Net.Maui(パッケージのバージョン:0.4.0)
  • ZXing.Net.Maui.Controls(パッケージのバージョン:0.4.0)

動作環境

  • iOS 17.3.1

問題の動作について

問題の動作について説明していきます。

まずはZXingでカメラを動作させます。以下のようにカメラが起動していることが分かります。

問題の動作説明1

このまま別の画面に移動すると、以下のようにカメラが動作しています。この画面にはカメラ機能が存在しないため、本来カメラは停止します。

問題の動作説明2

実装方法

実装方法を説明していきます。当記事では「CameraBarcodeReaderView」をカスタムしハンドラーを利用していきます。

ファイル構成について

以下のようにクラスファイルを用意します。

ファイル構造

各ファイルの内容について

「CameraBarcodeReaderView」のカスタマイズは以下のようになります。「IsCameraEnabled」はカメラの動作状況を制御しています。

using ZXing.Net.Maui.Controls;

namespace CustomIosZXing.CustomControls;

public class IosCameraBarcodeReaderView : CameraBarcodeReaderView
{
    public static readonly BindableProperty IsCameraEnabledProperty =
        BindableProperty.Create(
            nameof(IsCameraEnabled),
            typeof(bool),
            typeof(IosCameraBarcodeReaderView),
            true);

    /// <summary>
    /// カメラの動作状況(iOS用)
    /// </summary>
    public bool IsCameraEnabled
    {
        get
        {
            return (bool)GetValue(IsCameraEnabledProperty);
        }
        set
        {
            SetValue(IsCameraEnabledProperty, value);
        }
    }
}

iOSのカメラを停止するために以下のマッパーをPage(ContentPage など)のコンストラクタに実装します。「IsCameraEnabled」にfalseを設定すると、カメラが停止するようになっています。

CameraBarcodeReaderViewHandler.CameraBarcodeReaderViewMapper.Add(
    nameof(IosCameraBarcodeReaderView.IsCameraEnabled),
    (handler, virtualView) =>
    {
        if (handler is IosCameraBarcodeReaderViewHandler customHandler)
        {
            if (virtualView is IosCameraBarcodeReaderView customView)
            {
                if (!customView.IsCameraEnabled)
                {
                    customHandler.StopCamera();
                }
            }
        }
    });

ハンドラーは以下のようになります。内容はiOSのカメラ停止になっています。

using ZXing.Net.Maui;

#if IOS
using UIKit;
#endif

namespace CustomIosZXing.CustomControls;

public class IosCameraBarcodeReaderViewHandler : CameraBarcodeReaderViewHandler
{
#if IOS
    protected override void DisconnectHandler(UIView nativeView)
    {
        base.DisconnectHandler(nativeView);
    }
#endif

    public void StopCamera()
    {
#if IOS
        DisconnectHandler(new UIView());
#endif
    }
}

カスタマイズしたハンドラーを利用するために、MauiProgramに対して以下の内容を記載します。

using CustomIosZXing.CustomControls;
using Microsoft.Extensions.Logging;
using ZXing.Net.Maui.Controls;

namespace CustomIosZXing;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureMauiHandlers(handlers =>
            {
#if IOS || ANDROID
                // ハンドラーを実装します。
                handlers.AddHandler<IosCameraBarcodeReaderView, IosCameraBarcodeReaderViewHandler>();
#endif
            })
            // ZXingを利用するために必要になります。
            .UseBarcodeReader();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

利用方法

ContentPageでの利用方法は以下のようになります。以下の利用方法ではAndroid・Windowsの問題にも対策しています。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomIosZXing.Pages.ZXingPage"
             Title="ZXingPage">
    <Grid x:Name="gridBarcode">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"></RowDefinition>
            <RowDefinition Height="100"></RowDefinition>
        </Grid.RowDefinitions>
        <Label
            x:Name="lblText"
            Grid.Row="1"
            Text="" />
    </Grid>
</ContentPage>

以下では、画面から離れる時(OnDisappearing)に「IsCameraEnabled」に対してfalseを設定しています。そうすることで画面から離れる時にカメラが停止されます。

using CustomIosZXing.CustomControls;
using ZXing.Net.Maui;

namespace CustomIosZXing.Pages;

public partial class ZXingPage : ContentPage
{
    private IosCameraBarcodeReaderView _cameraBarcodeReader;

    public ZXingPage()
    {
        InitializeComponent();

        // カメラ停止用のマッパー
        CameraBarcodeReaderViewHandler.CameraBarcodeReaderViewMapper.Add(
            nameof(IosCameraBarcodeReaderView.IsCameraEnabled),
            (handler, virtualView) =>
            {
                if (handler is IosCameraBarcodeReaderViewHandler customHandler)
                {
                    if (virtualView is IosCameraBarcodeReaderView customView)
                    {
                        if (!customView.IsCameraEnabled)
                        {
                            customHandler.StopCamera();
                        }
                    }
                }
            });
    }

    /// <summary>
    /// 画面表示時
    /// </summary>
    protected override void OnAppearing()
    {
        base.OnAppearing();

        this.lblText.Text = "";

        /*
         * IosCameraBarcodeReaderViewを追加します。
         */
        _cameraBarcodeReader = new IosCameraBarcodeReaderView();
        _cameraBarcodeReader.Options = new BarcodeReaderOptions
        {
            Formats = BarcodeFormats.TwoDimensional,
        };
        _cameraBarcodeReader.BarcodesDetected += _cameraBarcodeReader_BarcodesDetected;
        this.gridBarcode.Add(_cameraBarcodeReader, row: 0);
    }

    private void _cameraBarcodeReader_BarcodesDetected(object? sender, BarcodeDetectionEventArgs e)
    {
        /*
         * スレッドが異なるためエラーになります。
         * そのエラーを回避するために「this.Dispatcher.Dispatch()」を利用します。
         */
        this.Dispatcher.Dispatch(() =>
        {
            foreach (BarcodeResult barcodeResult in e.Results)
            {
                this.lblText.Text = barcodeResult.Value;
            }
        });
    }

    /// <summary>
    /// 画面から離れる時
    /// </summary>
    protected override void OnDisappearing()
    {
        // カメラを停止します。
        _cameraBarcodeReader.IsCameraEnabled = false;
        this.gridBarcode.Remove(_cameraBarcodeReader);

        base.OnDisappearing();
    }
}

おわりに

.NET MAUIで二次元バーコードの読み込みにZXingを利用していましたが、iOSでカメラが停止しなかったので困っていました・・・

当記事の内容を利用することで対応することができました。同じような境遇の方の役に立てば幸いです。