はじめに
アプリ内にPDFのドキュメントを置きたい場合、ブラウザ遷移による表示とアプリ内でのWebView表示の2パターンが考えられます。
リンクからブラウザ遷移させるのが実装としては一番楽ですが、ブラウザからアプリに戻るのが面倒であるため、ユーザの手間を考えるとなるべくこの方法は使いたくありません。
そうなると、WebViewでの表示一択となりますが、ここで1つ問題があります。
それはAndroidではWebViewにおけるPDF表示に非対応であるということです。
この問題を回避してAndroidでもアプリ内でPDFを表示する方法を2つ記載します。
Google Docs Viewerの使用
1つ目は、Google Docs Viewerを使って、PDFをページ内に埋め込んで表示する方法です。
AndroidのときだけURLをエンコードして、Google Docs ViewerのURLに埋め込むような関数viewerUri()
を作成します。
import { Platform } from "react-native";
export const viewerUri = (uri: string) => {
if (Platform.OS === "android") {
const encodeUri = encodeURI(uri);
return `https://docs.google.com/viewer?url=${encodeUri}&embedded=true`;
}
return uri;
};
呼んできたuri
を元にWebView
を表示するようなテンプレート WebViewTemplate
を作成します。
export const WebViewTemplate = ({ route }) => {
const { uri } = route.params;
return <WebView source={uri} />;
};
WebViewTemplate
の呼び出し元でviewerUri()
を使用します。
navigate("WebViewTemplate", {
uri: viewerUri(<path>),
title: <title>,
});
これでAndroidのWebViewでPDFを表示できるようになったのですが、実際に実機を触ってみるとPDFのファイルサイズによっては開くのに数秒以上かかってしまっており、**「いつ開くんだよ...(イライラ)」**という感情が生じてしまいます。
UXを考慮するとこれが結構見過ごせないレベルのモタつきだったので、別の方法(2つ目)を模索することになりました。
rn-pdf-reader-js
の使用
2つ目は、rn-pdf-reader-js
というライブラリに頼る方法です。
expo install rn-pdf-reader-js
でインストールしたら、テンプレート PdfViewTemplate
に以下のように記述します。
import React, { useCallback, useState } from "react";
import { StyleSheet } from "react-native";
import PDFReader from "rn-pdf-reader-js";
import { LoadingIcon } from "src/components/molecules/LoadingIcon"
export const PdfViewTemplate = ({ route }) => {
const { uri } = route.params;
const [isLoading, setIsLoading] = useState(true);
const handleOnLoadEnd = useCallback(() => setIsLoading(false), []);
return (
<>
<PDFReader
source={{ uri }}
style={StyleSheet.absoluteFill}
noLoader
onLoadEnd={handleOnLoadEnd}
/>
{isLoading && <LoadingIcon style={[StyleSheet.absoluteFill, styles.loading]} />}
</>
);
};
const styles = StyleSheet.create({
loading: {
justifyContent: "center",
alignItems: "center",
},
});
Google Docs Viewerを使用するときよりも表示速度は速くなりますが、それでも1~2秒は描画に時間がかかります。
そんなときにローディングアイコン(クルクルしたやつ)を出したくなると思いますが、PDFReader
にはすでにそのアイコンが実装されています。
中には自分でカスタムしたアイコンを出したい方もいると思いますが、そんな場合には、以下のようにisLoading
というstateを用意して、PDFReader
のonLoadEnd
というpropsでローディングの終了を取得するようにすればOKです。
URLのエンコードは必要ないので、呼び出し元にはパスをそのまま記述します。
navigate("PdfViewTemplate", {
uri: <path>,
title: <title>,
});
これでモタつきなくPDFを開けるようになりました。
react-native-pdf-view
の使用
rn-pdf-reader-js
でPDFの表示は速くなったのですが、今度はAndroid側のページ操作がスクロールではなく、矢印タッチになってしまいました。
iOSとAndroidのUIの差分が気になるだけではなく単純に操作がしづらいので、これらの問題を解消するためにはまた別のライブラリreact-native-pdf-view
を利用する必要があります。
ただ、こちらをExpoのプロジェクトに導入するためには、ejectでBare workflowに変更する必要があるため、あくまで奥の手となります。
最後に
よりよい方法をご存知の方がいらっしゃいましたら、コメント欄で教えていただければ幸いです。
参考資料