メインコンテンツへスキップ

画像

静的画像リソース

React Nativeは、AndroidおよびiOSアプリで画像やその他のメディア資産を管理するための統一された方法を提供します。アプリに静的画像を追加するには、ソースコードツリーのどこかに配置し、次のように参照します。

tsx
<Image source={require('./my-icon.png')} />

画像名はJSモジュールが解決されるのと同じ方法で解決されます。上記の例では、バンドラはそれをrequireするコンポーネントと同じフォルダ内にある`my-icon.png`を探します。

`@2x`と`@3x`のサフィックスを使用して、異なる画面密度の画像を提供できます。もし次のようなファイル構造がある場合:

.
├── button.js
└── img
├── check.png
├── check@2x.png
└── check@3x.png

...そして`button.js`のコードに以下が含まれている場合:

tsx
<Image source={require('./img/check.png')} />

...バンドラはデバイスの画面密度に対応する画像をバンドルして提供します。例えば、`check@2x.png`はiPhone 7で使用され、`check@3x.png`はiPhone 7 PlusやNexus 5で使用されます。画面密度に一致する画像がない場合は、最も近い最適なオプションが選択されます。

Windowsでは、プロジェクトに新しい画像を追加した場合、バンドラを再起動する必要があるかもしれません。

これによって得られるいくつかの利点は以下の通りです。

  1. AndroidとiOSで同じシステムを使用できます。
  2. 画像はJavaScriptコードと同じフォルダに存在します。コンポーネントは自己完結型です。
  3. グローバルな名前空間がないため、名前の衝突を心配する必要がありません。
  4. 実際に使用される画像のみがアプリにパッケージ化されます。
  5. 画像の追加や変更にアプリの再コンパイルは不要で、通常通りシミュレータをリフレッシュできます。
  6. バンドラが画像の寸法を知っているため、コード内でそれを二重に記述する必要がありません。
  7. 画像はnpmパッケージを介して配布できます。

これが機能するためには、`require`内の画像名は静的に知られている必要があります。

tsx
// GOOD
<Image source={require('./my-icon.png')} />;

// BAD
const icon = this.props.active
? 'my-icon-active'
: 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;

// GOOD
const icon = this.props.active
? require('./my-icon-active.png')
: require('./my-icon-inactive.png');
<Image source={icon} />;

この方法でrequireされた画像ソースには、画像のサイズ(幅、高さ)情報が含まれていることに注意してください。画像を動的に(例えばflexを介して)スケーリングする必要がある場合は、style属性に手動で`{width: undefined, height: undefined}`を設定する必要があるかもしれません。

静的な非画像リソース

上記で説明した`require`構文は、オーディオ、ビデオ、またはドキュメントファイルをプロジェクトに静的に含めるためにも使用できます。`.mp3`、`.wav`、`.mp4`、`.mov`、`.html`、`.pdf`など、最も一般的なファイルタイプがサポートされています。完全なリストについては、バンドラのデフォルト設定を参照してください。

Metroの設定`assetExts`リゾルバオプションを追加することで、他のタイプのサポートを追加できます。

注意点として、現在、非画像アセットにはサイズ情報が渡されないため、ビデオは`flexGrow`ではなく絶対配置を使用する必要があります。この制限は、XcodeまたはAndroidのAssetsフォルダに直接リンクされているビデオには発生しません。

ハイブリッドアプリのリソースからの画像

ハイブリッドアプリ(一部のUIがReact Native、一部がプラットフォームコード)を構築している場合でも、アプリに既にバンドルされている画像を使用できます。

XcodeのアセットカタログまたはAndroidのdrawableフォルダに含まれる画像の場合、拡張子なしの画像名を使用します。

tsx
<Image
source={{uri: 'app_icon'}}
style={{width: 40, height: 40}}
/>

Androidのassetsフォルダ内の画像の場合、`asset:/`スキームを使用します。

tsx
<Image
source={{uri: 'asset:/app_icon.png'}}
style={{width: 40, height: 40}}
/>

これらのアプローチは安全性のチェックを提供しません。これらの画像がアプリケーションで利用可能であることを保証するのはあなた次第です。また、画像の寸法を手動で指定する必要があります。

ネットワーク画像

アプリで表示する画像の多くは、コンパイル時には利用できないか、バイナリサイズを小さく保つために動的に読み込みたい場合があります。静的リソースとは異なり、*画像の寸法を手動で指定する必要があります*。iOSのApp Transport Securityの要件を満たすため、httpsを使用することが強く推奨されます。

tsx
// GOOD
<Image source={{uri: 'https://react.dokyumento.jp/logo-og.png'}}
style={{width: 400, height: 400}} />

// BAD
<Image source={{uri: 'https://react.dokyumento.jp/logo-og.png'}} />

画像のネットワークリクエスト

画像リクエストと共にHTTP-Verb、ヘッダー、またはボディなどを設定したい場合は、sourceオブジェクトにこれらのプロパティを定義することで行うことができます。

tsx
<Image
source={{
uri: 'https://react.dokyumento.jp/logo-og.png',
method: 'POST',
headers: {
Pragma: 'no-cache',
},
body: 'Your Body goes here',
}}
style={{width: 400, height: 400}}
/>

URIデータ画像

REST API呼び出しからエンコードされた画像データを取得することがあります。これらの画像を使用するには、`'data:'`URIスキームを使用できます。ネットワークリソースと同様に、*画像の寸法を手動で指定する必要があります*。

情報

これは、DBからのリスト内のアイコンのような、非常に小さく動的な画像にのみ推奨されます。

tsx
// include at least width and height!
<Image
style={{
width: 51,
height: 51,
resizeMode: 'contain',
}}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==',
}}
/>

キャッシュ制御

場合によっては、画像がローカルキャッシュに既にある場合にのみ表示したいことがあります。たとえば、高解像度のものが利用可能になるまでの低解像度のプレースホルダーなどです。他のケースでは、画像が古くても気にせず、帯域幅を節約するために古い画像を表示しても構わない場合があります。`cache` sourceプロパティを使用すると、ネットワーク層がキャッシュとどのように対話するかを制御できます。

  • default: ネイティブプラットフォームのデフォルト戦略を使用します。
  • reload: URLのデータは元のソースから読み込まれます。既存のキャッシュデータは、URLロードリクエストを満たすために使用されるべきではありません。
  • force-cache: 既存のキャッシュデータは、その有効期限や古さに関係なく、リクエストを満たすために使用されます。キャッシュにリクエストに対応する既存のデータがない場合、データは元のソースから読み込まれます。
  • only-if-cached: 既存のキャッシュデータは、その有効期限や古さに関係なく、リクエストを満たすために使用されます。URLロードリクエストに対応する既存のデータがキャッシュにない場合、元のソースからデータを読み込む試みは行われず、ロードは失敗したとみなされます。
tsx
<Image
source={{
uri: 'https://react.dokyumento.jp/logo-og.png',
cache: 'only-if-cached',
}}
style={{width: 400, height: 400}}
/>

ローカルファイルシステムの画像

`Images.xcassets`の外にあるローカルリソースを使用する例については、CameraRollを参照してください。

Drawableリソース

Androidは`xml`ファイルタイプを介してdrawableリソースの読み込みをサポートしています。これは、アイコンのレンダリングにベクタードローアブルを使用したり、図形の描画にシェイプドローアブルを使用できることを意味します!これらのリソースタイプは、他の静的リソースハイブリッドリソースと同様にインポートして使用できます。画像の寸法は手動で指定する必要があります。

JSコードと同じ場所に存在する静的drawableの場合、`require`または`import`構文を使用します(どちらも同じように機能します)。

tsx
<Image
source={require('./img/my_icon.xml')}
style={{width: 40, height: 40}}
/>

Androidのdrawableフォルダ(例:`res/drawable`)に含まれるdrawableの場合、拡張子なしのリソース名を使用します。

tsx
<Image
source={{uri: 'my_icon'}}
style={{width: 40, height: 40}}
/>

drawableリソースと他の画像タイプとの1つの重要な違いは、Androidがアセットをパッケージ化するためにAndroid Asset Packaging Tool (AAPT) を実行する必要があるため、アセットはAndroidアプリケーションのコンパイル時に参照されなければならない点です。AAPTが作成するバイナリXMLファイル形式は、Metroを介してネットワーク経由で読み込むことはできません。アセットのディレクトリや名前を変更した場合は、その都度Androidアプリケーションを再ビルドする必要があります。

XML drawableリソースの作成

Androidは、Drawableリソースガイドで、サポートされている各drawableリソースタイプに関する包括的なドキュメントと、生のXMLファイルの例を提供しています。Android StudioのVector Asset Studioのようなツールを利用して、Scalable Vector Graphic (SVG) やAdobe Photoshop Document (PSD) ファイルからベクタードローアブルを作成できます。

情報

XMLファイルを静的画像リソースとして(つまり`import`や`require`文で)扱いたい場合は、作成するXMLファイル内で他のリソースを参照することは避けるべきです。色の状態リストディメンションリソースのような他のdrawableや属性への参照を利用したい場合は、drawableをハイブリッドリソースとして含め、名前でインポートする必要があります。

最適なカメラロール画像

iOSはカメラロールに同じ画像に対して複数のサイズを保存します。パフォーマンス上の理由から、できるだけ近いサイズのものを選択することが非常に重要です。200x200のサムネイルを表示する際に、フル品質の3264x2448の画像をソースとして使用したくはないでしょう。完全に一致するものがあれば、React Nativeはそれを選びます。そうでなければ、近いサイズからのリサイズによるぼやけを避けるために、少なくとも50%大きい最初のものを使用します。これはすべてデフォルトで行われるため、これを自分で行うための面倒で(そしてエラーを起こしやすい)コードを書く心配はありません。

なぜすべてを自動的にサイズ調整しないのか?

ブラウザでは、画像にサイズを指定しないと、ブラウザは0x0の要素をレンダリングし、画像をダウンロードしてから正しいサイズで画像をレンダリングします。この挙動の大きな問題は、画像が読み込まれるにつれてUIが飛び跳ねることで、これは非常に悪いユーザー体験をもたらします。これはCumulative Layout Shift(CLS、累積レイアウトシフト)と呼ばれます。

React Nativeでは、この挙動は意図的に実装されていません。開発者にとって、リモート画像の寸法(またはアスペクト比)を事前に知ることはより多くの作業を伴いますが、それがより良いユーザー体験につながると私たちは信じています。`require('./my-icon.png')`構文を介してアプリバンドルから読み込まれる静的画像は、マウント時に寸法がすぐに利用可能であるため、*自動的にサイズ調整できます*。

例えば、`require('./my-icon.png')`の結果は次のようになるかもしれません。

tsx
{"__packager_asset":true,"uri":"my-icon.png","width":591,"height":573}

オブジェクトとしてのソース

React Nativeにおける興味深い決定の一つは、`src`属性が`source`という名前で、文字列ではなく`uri`属性を持つオブジェクトを取ることです。

tsx
<Image source={{uri: 'something.jpg'}} />

インフラストラクチャ側では、その理由は、このオブジェクトにメタデータを添付できるからです。例えば、`require('./my-icon.png')`を使用している場合、その実際の場所やサイズに関する情報を追加します(この事実に依存しないでください、将来変更される可能性があります!)。これは将来の拡張性のためでもあります。例えば、ある時点でスプライトをサポートしたい場合、`{uri: ...}`を出力する代わりに、`{uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}}`を出力し、既存のすべての呼び出し箇所で透過的にスプライトをサポートできます。

ユーザー側では、これにより、表示されるサイズを計算するために画像の寸法などの有用な属性をオブジェクトに注釈付けできます。あなたの画像に関するより多くの情報を格納するためのデータ構造として自由に使用してください。

ネストによる背景画像

Webに詳しい開発者からよくある機能リクエストは`background-image`です。このユースケースを扱うために、`<Image>`と同じプロパティを持つ`<ImageBackground>`コンポーネントを使用し、その上に重ねたい子要素を追加することができます。

実装が基本的なため、場合によっては`<ImageBackground>`を使用したくないかもしれません。詳細については`<ImageBackground>`のドキュメントを参照し、必要に応じて独自のカスタムコンポーネントを作成してください。

tsx
return (
<ImageBackground source={...} style={{width: '100%', height: '100%'}}>
<Text>Inside</Text>
</ImageBackground>
);

widthとheightのスタイル属性をいくつか指定する必要があることに注意してください。

iOSの角丸スタイル

以下の角に特化した角丸スタイルプロパティは、iOSの画像コンポーネントによって無視される可能性があることに注意してください。

  • borderTopLeftRadius
  • borderTopRightRadius
  • borderBottomLeftRadius
  • borderBottomRightRadius

オフスレッドでのデコード

画像のデコードには1フレーム以上の時間がかかることがあります。これはWeb上でフレーム落ちの主要な原因の一つです。なぜなら、デコードはメインスレッドで行われるからです。React Nativeでは、画像のデコードは別のスレッドで行われます。実際には、画像がまだダウンロードされていないケースをすでに処理する必要があるため、デコード中にプレースホルダーをさらに数フレーム表示しても、コードの変更は必要ありません。

iOSの画像キャッシュ上限の設定

iOSでは、React Nativeのデフォルトの画像キャッシュ上限を上書きするためのAPIを公開しています。これはネイティブのAppDelegateコード内(例えば`didFinishLaunchingWithOptions`内)から呼び出す必要があります。

objectivec
RCTSetImageCacheLimits(4*1024*1024, 200*1024*1024);

パラメータ

名前必須説明
imageSizeLimitnumberはい画像キャッシュサイズの上限。
totalCostLimitnumberはい総キャッシュコストの上限。

上記のコード例では、画像サイズの上限は4MB、総コストの上限は200MBに設定されています。