0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React Nativeで招待QRコードをPNGにして共有する

0
Last updated at Posted at 2026-08-07

アプリの招待リンクをQRコードで表示し、そのまま画像として共有できるようにしました。

QRコードの表示にはreact-native-qrcode-svg、ViewのPNG化にはreact-native-view-shot、共有シートにはexpo-sharingを使っています。

npx expo install expo-sharing react-native-view-shot
pnpm add react-native-qrcode-svg react-native-svg

QRコードだけでなく団体名も画像へ入れる

共有された画像だけを見ても内容が分かるよう、QRコードと団体名を1つのViewへ入れています。

const captureAreaRef = useRef<View>(null);

<View
  ref={captureAreaRef}
  collapsable={false}
  style={styles.captureArea}
>
  <Text style={styles.orgName}>{orgName}</Text>
  <QRCode
    value={inviteUrl}
    size={200}
    color={colors.foreground}
    backgroundColor={colors.card}
  />
</View>

collapsable={false}を付けているのは、React NativeがネイティブViewを最適化で省略し、captureRefの対象を取得できなくなるのを避けるためです。

背景色も明示しています。透過画像のまま共有先の背景が暗いと、QRコードの余白や団体名が読みにくくなるためです。

ViewをPNGとして保存する

共有ボタンではcaptureRefへViewのrefを渡します。

const captured = await captureRef(captureAreaRef, {
  format: "png",
  quality: 1,
});

返ってきた値は一時ファイルのパスです。Androidではschemeなしのパスが返る場合があったため、共有前にfile://を補っています。

const uri = captured.startsWith("file://")
  ? captured
  : `file://${captured}`;

expo-sharingで共有する

端末で共有機能を利用できるか確認してから共有シートを開きます。

if (await Sharing.isAvailableAsync()) {
  await Sharing.shareAsync(uri, {
    mimeType: "image/png",
    dialogTitle: `${orgName}の招待QRコード`,
  });
} else {
  await Share.share({ url: uri });
}

実際の処理では、連打で複数の共有処理が始まらないようstateも持たせています。

const [sharing, setSharing] = useState(false);

const handleShareImage = async () => {
  if (sharing) return;
  setSharing(true);

  try {
    // captureRefとshareAsync
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    Alert.alert("共有に失敗しました", message);
  } finally {
    setSharing(false);
  }
};

処理中はボタンをdisabledにし、ラベルを「共有の準備中」に切り替えています。キャプチャから共有シートが開くまで少し間があるため、無反応に見せないための対応です。

QRコードへ何を入れるか

QRコードにはcustom schemeではなく、Webでも開けるHTTPSの招待URLを入れています。

const inviteUrl = `${config.apiUrl}/invite/${orgId}`;

iOSではUniversal Linksを設定しているため、アプリが入っていれば招待画面をアプリで開きます。アプリがなければWeb版を表示します。

QR画像はスクリーンショットではありません。キャプチャ対象を専用のViewへ限定しているので、モーダルの閉じるボタンや「画像を共有」ボタンは画像に入りません。

実装全体

要点をまとめると次の形です。

export function InviteQrModal({ orgName, inviteUrl }: Props) {
  const captureAreaRef = useRef<View>(null);
  const [sharing, setSharing] = useState(false);

  const share = async () => {
    if (sharing) return;
    setSharing(true);
    try {
      const path = await captureRef(captureAreaRef, {
        format: "png",
        quality: 1,
      });
      const uri = path.startsWith("file://") ? path : `file://${path}`;
      await Sharing.shareAsync(uri, { mimeType: "image/png" });
    } finally {
      setSharing(false);
    }
  };

  return (
    <>
      <View ref={captureAreaRef} collapsable={false}>
        <Text>{orgName}</Text>
        <QRCode value={inviteUrl} size={200} />
      </View>
      <Pressable onPress={share} disabled={sharing}>
        <Text>{sharing ? "共有の準備中..." : "画像を共有"}</Text>
      </Pressable>
    </>
  );
}

QRコードのSVGを直接ファイルへ変換するより、表示用のViewをそのままキャプチャする方が、団体名や説明を含めた共有画像を作りやすい構成でした。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?