Expoでオリジナルのベクターアイコンを使う


Expo、およびReact Nativeでベクターアイコンを使うためには、このようなパッケージが用意されています。

https://github.com/expo/vector-icons
https://github.com/oblador/react-native-vector-icons

import React from 'react';
import { Ionicons } from '@expo/vector-icons'; // アイコンセットをimport

export default class IconExample extends React.Component {
  render() {
    return <Ionicons name="md-checkmark-circle" size={32} color="green" />;
  }
}

好みのアイコンセットをimportし、アイコンの名前などをpropsで指定します。

デフォルトで多くのアイコンが使用でき、一覧はこちらから確認できます。
https://oblador.github.io/react-native-vector-icons/
ただし気に入るアイコンが無い場合や、必要なアイコンのみ読み込みたい場合、あるいはデザイナーに指定されたものがある場合など、オリジナルでアイコンセットを作成したいシチュエーションもあるかと思います。
上記のパッケージでは内部的にはアイコンフォント化されたttfファイルを使用しているので、オリジナルのアイコンを使いたい場合はフォントファイルを用意すればWebと同じように使用できます。

iconfontの生成

icomoon等をつかってもよいですが、SVGファイルから動的に作成できるようにしてみましょう。

SVGファイルの用意

ディレクトリを作り、SVGファイルをまとめます。

.
└── icons
    ├── close.svg
    ├── arrow.svg
    ├── left-arrow.svg
    └── ...

SVGファイルの注意点として、

  • アートボードのサイズはそこそこに(だいたい100 x 100くらい以上)
    小さいと小数点以下が切り捨てられて歪なアイコンになってしまう場合があります。
  • パスはアウトライン化し、交差の無い単純なパスにする
  • 全てのアイコンがアートボードの縦サイズで合わせれるのを考慮してアイコンを配置
    デザイン次第ですが、あらかじめソース側でサイズの指定が楽になるように考えておきましょう。

等。Webのナレッジと同様です。

SVGからアイコンフォントファイルを生成

Expo/RNで使用するためにはアイコン名と文字コードを紐付けるデータが必要です。
フォントファイルと一緒に、お手軽にJSONファイルも出力してくれるicon-font-generatorというパッケージを使用します。

$ npm install --dev icon-font-generator

コマンドラインから使用できるので、package.jsonにこのようにスクリプトを追加します。

package.json
  "scripts": {
    "iconfont": "icon-font-generator icons/*.svg -o assets/fonts --jsonpath js/constants/iconfonts.json --css false --html false --types ttf",
  }

assets/fontsディレクトリにアイコンフォントファイルが、
js/constantsディレクトリにアイコン名と文字コードを紐付けるJSONファイルが出力されるようにしました。
好みで場所を変更し、あらかじめディレクトリを作成した状態でコマンドを叩けばファイルが作成されるはずです。
アイコンを追加したタイミングでnpm run iconfontでもいいと思いますが、使いやすいタイミングでrunするように設定します。

Expoから使用する

Iconコンポーネントを作成します。

js/components/Icon.js
import React from "react";
import { createIconSet } from "@expo/vector-icons";
import map from "../constants/iconfonts"; // 生成されたJSONファイル

// icon-font-generatorで生成されたJSONファイルをcreateIconSetに渡すために変換
const glyphMap = {};
Object.keys(map).forEach(key => {
  glyphMap[key] = parseInt(map[key].substring(1), 16);
});

const expoAssetId = require("../../assets/fonts/icons.ttf"); // アイコンファイルをExpoで読み込み

const Icon = createIconSet(glyphMap, "iconfont", expoAssetId);

export default Icon;

icon-font-generatorで生成されたTTFファイルとJSONファイルとを読み込み、@expo/vector-iconscreateIconSetに渡してReactコンポーネントを作成します。
生成されたJSONファイルはそのままではなく、文字コードの部分を数値に変換して使用します。

使ってみる

nameには元のSVG画像のファイル名を指定し、あとは通常のアイコンセットと同じように使用できます。

js/components/HeaderBackButton.jsx
import React from "react";
import Icon from "./Icon";
import { StyleSheet, TouchableWithoutFeedback } from "react-native";

const styles = StyleSheet.create({
  icon: {
    flex: 1,
    padding: 10,
    marginLeft: 6
  }
});

/**
 * 戻るボタン
 * @param {object} props
 * @returns {JSX}
 */
export default props => (
  <TouchableWithoutFeedback onPressIn={props.onBack}>
    <Icon
      name="left-arrow"
      style={styles.icon}
      color="#fff"
      size={24}
    />
  </TouchableWithoutFeedback>
);