はじめに
React Nativeで新作アプリを作りました。
久しぶりにカスみたいなアプリ作りました
— yoshii🐍 (@ganja_tuber) January 5, 2022
姓名判断っぽい画像を捏造できます
12/30に完成したんですが、Appleさんに怪しい占いアプリと勘違いされ、なかなか出せませんでした#嘘姓名判断
iOShttps://t.co/2gUSUI2rQC
Androidhttps://t.co/IyslOklSkl pic.twitter.com/kG0NxRcMkW
嘘の姓名判断の結果を捏造できるおふざけアプリです。
よかったら遊んでみてください。
困ったこと
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
: カメラロールに画像を保存します
以下コードです。
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から画像を保存してます。
const uri = await captureRef(viewShot);
await MediaLibrary.saveToLibraryAsync(uri);
現在(2022/01/08)、iOSだとカメラロールに保存する処理が走ると初回には勝手に保存を許可するかユーザに尋ねるAlertが出てくれるので、ぶっちゃけこの2行だけでいいです。
ただ、Androidだと少し話が変わってきます。
以下の1行でしっかり許可を要求する必要があります。
const permissionResponse = await MediaLibrary.requestPermissionsAsync();
また、保存の許可を求めるAlertは何回も出しちゃいけないことになってるので、2回目以降は
permissionAlert
関数を使って設定から許可し直すことを要求するという形にしてます。
注意
開発環境でExpo Goを使って動作確認をしているときに、1回目の保存の許可を求めるAlertを拒否した場合、設定から再度許可しても保存できないようです。
1回目のAlertで保存を拒否した後の動作確認がしたい人は yarn build
して、静的ファイルを生成して確認するのが確実で良いでしょう。
まとめ
React NativeでViewを画像に変換して保存する方法を書きました。
これを使えば作れるアプリの幅が広がると思います。
わからないことや、問題があればコメントお願いします。
では、さようなら〜