LoginSignup
3
1

More than 1 year has passed since last update.

Expo (React Native) でViewを画像に変換してデバイスに保存する

Last updated at Posted at 2022-01-08

はじめに

React Nativeで新作アプリを作りました。

嘘の姓名判断の結果を捏造できるおふざけアプリです。

好きなように運勢を選択して…
65-3.png

こんな感じの適当な姓名判断を作れます
IMG_4899.jpg

よかったら遊んでみてください。

困ったこと

React NativeでViewを画像に変換し、デバイスに保存するのに手間取ったので、
実装方法を書きます。

(Androidは実機を持っていないので、動かなかったらコメントとかで教えてください。一応シミュレーションでは動作確認してます。)

GitHub リポジトリ

解説するコードは全て以下のリポジトリにまとめてます。

実装方法

前提

Expo CLIを入れておいてください

生のReact Native派の人は適宜読み替えてください
あと、yarn使います。npmの人も適宜読み替えてください。

プロジェクト作成

まずは、expo init

ターミナルに以下を入力

expo init ExpoConvertImageSample

ExpoConvertImageSampleのとこは適当に決めてください。

エンター押したらなんか聞かれるので、blank (TypeScript) を選びます。
TypeScriptじゃなかったり、react navigationしたい人は適宜変えてね。

実装

ライブラリ導入

yarn add react-native-view-shot expo-media-library

react-native-view-shot: Viewを画像に変換します

expo-media-library: カメラロールに画像を保存します

以下コードです。

App.tsx
import React, { useCallback, useRef, VFC } from "react";
import { View, Text, Button, Platform, Alert } from "react-native";
import ViewShot, { captureRef } from "react-native-view-shot";
import * as MediaLibrary from "expo-media-library";

const os = Platform.OS;

const App: VFC = () => {
  const viewShot = useRef(null);

  const permissionAlert = () => {
    Alert.alert(
      "画像の保存が許可されませんでした。",
      "設定から画像の保存を許可してください。"
    );
  };

  const saveImageFromView = async () => {
    try {
      const uri = await captureRef(viewShot);
      await MediaLibrary.saveToLibraryAsync(uri);
      Alert.alert("画像の保存完了");
    } catch (e) {
      console.log(e);
      permissionAlert();
    }
  };

  const capture = useCallback(async () => {
    // android
    if (os === "android") {
      const permission = await MediaLibrary.getPermissionsAsync();
      if (permission.canAskAgain && permission.status !== "granted") {
        const permissionResponse = await MediaLibrary.requestPermissionsAsync();
        if (permissionResponse.status !== "granted") {
          permissionAlert();
        } else {
          await saveImageFromView();
        }
      } else if (permission.status !== "granted") {
        permissionAlert();
      } else {
        await saveImageFromView();
      }
    }
    // iOS
    else {
      await saveImageFromView();
    }
  }, []);

  return (
    <View>
      <ViewShot
        ref={viewShot}
        style={{ height: 500, width: 300, backgroundColor: "red" }}
      >
        <Text style={{ fontSize: 30, marginTop: 200, textAlign: "center" }}>
          {"こんにちは〜"}
        </Text>
      </ViewShot>
      <Button title="変換して保存する" onPress={capture} />
    </View>
  );
};

export default App;

上記のコードをApp.tsxにコピペして、

yarn start

して、シミュレーター起動してボタンを押したらカメラロールに画像が保存されるはずです。

解説

やっていることの根幹は、saveImageFromView関数内の以下の2行です。
1行目でref属性にviewShotを指定しているViewをキャプチャーして、uriを取得。
2行目でそのuriから画像を保存してます。

App.tsx
const uri = await captureRef(viewShot);
await MediaLibrary.saveToLibraryAsync(uri);

現在(2022/01/08)、iOSだとカメラロールに保存する処理が走ると初回には勝手に保存を許可するかユーザに尋ねるAlertが出てくれるので、ぶっちゃけこの2行だけでいいです。

iOSで許可を求める様子
Simulator Screen Shot - iPhone 11 Pro Max - 2022-01-08 at 13.32.27.png

ただ、Androidだと少し話が変わってきます。

以下の1行でしっかり許可を要求する必要があります。

App.tsx
const permissionResponse = await MediaLibrary.requestPermissionsAsync();

androidで許可を求める様子
スクリーンショット 2022-01-08 14.37.26.png

また、保存の許可を求めるAlertは何回も出しちゃいけないことになってるので、2回目以降は
permissionAlert 関数を使って設定から許可し直すことを要求するという形にしてます。

注意

開発環境でExpo Goを使って動作確認をしているときに、1回目の保存の許可を求めるAlertを拒否した場合、設定から再度許可しても保存できないようです。

1回目のAlertで保存を拒否した後の動作確認がしたい人は yarn buildして、静的ファイルを生成して確認するのが確実で良いでしょう。

まとめ

React NativeでViewを画像に変換して保存する方法を書きました。
これを使えば作れるアプリの幅が広がると思います。
わからないことや、問題があればコメントお願いします。

では、さようなら〜

3
1
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
3
1