Assets.xcassetsの使い方わかってなくてごめんなさい
# Assets とは
Assets とはソースコード以外のリソースファイルを管轄するための Xcode10 くらいから与えられた仕組みのこと。
これ以前は例えば画像を Swift から呼び出そうとしたら、
- 適当に Resources フォルダを作る
- その中に登録したい画像を突っ込む
- ただし、デバイスの解像度に合わせて複数用意しなければいけない!!
- 画像を呼び出せるコードを書く
- 実際にコードから呼び出せる
という手順を踏んでいた。これが非常にめんどくさくて、まあできないこともなかったのだがデバイスの解像度に合わせて画像を複数用意するところがいかんせんめんどくさかった。プロジェクトを複数で管理しているとここでコンフリクトばっかりすることもしばしば。
それに対して Assets であれば画像の登録も簡単で、コンフリクトは発生しないし、Assets に登録した時点で簡単に呼び出すことができる。例えばNyamo
という画像を Assets 内に突っ込んでしまえばImage("Nyamo")
でその画像を呼び出すことができる。
つまり、とても便利なわけである。
が、ぼくはこの便利さに甘えてこれをさらに有効活用するということを忘れてしまっていました。この使い方をしているだけではいろいろと問題点があり、それを解消するために黒魔術的なわけのわからない倒錯をしていたので、後世にそれを伝えないためにも Assets の正しい使い方を布教していこうと思う。
# 開発中に発生した問題点
さてAAAAA
という名前で画像ファイルを登録すればImage("AAAAA")
という名前で SwiftUI から画像を呼び出せることは先程述べたのであるが、ファイル数が 1 つや 2 つならこれでいいが、もっと増えたときにややこしいことになるのがわかるだろうか。
一つは、画像呼び出しが増えれば Typo をしてしまい、正しい画像が呼び出せなくなる可能性があること(その場合は無が表示される)。もう一つは、必然的に画像のアセット名が被る可能性があるということだ。
例えば開発していた Salmonia3 では当初すべての画像を任天堂のサーバから受け取るようにしていたが使用していた URLImage のライブラリが画像枚数が増えると重くなったり読み込みが正しくできないという問題が発生したため、結局 Salmonia1 のようにローカルから呼び出す方法に変更することになった。
で、このときに発生したのがアセット名が被ってしまうという問題だった。
今回の例でいうとステージ名とブキ名がID=5000
で被ってしまうのでImage("5000")
として呼び出すとステージとしての ID5000 なのか、ブキとしての ID5000 なのかがわからないということである。
これを解決するための苦肉の策として、ブキにはw5000
というように別の名前を割り当てて使っていました。しかし、これはどうもスマートではない、そう考えながらもこのクソみたいな対策で乗り切っていました。
# Namespace
そんな中、別件で作業しているときにこの記事 (opens new window)を発見しました。
これは SVG を Assets に登録してそれを呼び出せるようにしようという内容の記事だったのですが、その中で、
Asset Catalog は形式がとても簡単です。Asset Catalog はフォルダーの「Provides Namespace」にチェックを入れるとその中身がネームスペースに入るので便利です
という文章を発見してしまったわけです。
# Namespace の利点
Namespace とは超大雑把に言えば学年におけるクラス(プログラミングの Class とは関係がない)のような存在です。
例えば、学年が 200 人もいれば名字が同じ生徒が存在する可能性がそこそこあります。「田中くん」と呼んでも「どの田中ですか?」となるわけです。さっきの例だと「Assets 名が 5000 の画像を呼び出そう」としても「ステージ画像の 5000 なのかブキ画像の 5000 なのか」がわからないわけです。
ところがここで Namespace を使って「四組の田中くん」と呼べば四組に田中くんが二人いない限りはどの田中くんが呼ばれたのかわかることになります。
Namespace について
もちろん、一つの Namespace に同一の名前が複数存在することは許されない(コンパイルエラーが発生する)ので、普通は Namespace という仕組みを使えばこの問題は解決される。
つまり、ステージ画像にはStage
という Namespace を与え、ブキ画像にはWeapon
という Namespace を与えれば良いことになります。で、この考え自体は前々からあったのですが、実装がめんどくさいと思って放置してきました。
まさかProvides Namespace
にチェックを入れるだけで自動的に Namespace が与えられるとは......
# Font Awesome (opens new window)
ここで、Font Awesome5 の画像を Assets に登録して呼び出すことを考えてみます。Font Awesome 5 は全てのシンボルの SVG ファイルが公開されているのでそれをダウンロードしてきます。
で、適当に好きなアセットを選択して Assets.xcassets にFA5
というフォルダを作り、そのフォルダの中に登録したい SVG ファイルを突っ込みます。
# 登録時の設定
SVG 画像はその他の画像と違い、いくつかの細かい設定ができるのでやっておきましょう。
# Resizing
SVG 画像は通常の画像とは違いベクター画像なのでPreserve Vector Data
にチェックを入れます。これでベクター画像としてリサイズされ、画質劣化せずに拡大縮小できます。
# Scales
通常の画像では三つの画像サイズを要求されますが、SVG であれば不要なのでSingle Scale
を選択します。
最後に、FA5
のフォルダに対してProvides Namespace
にチェックをいれます。するとフォルダが通常の黄色から青い色に変化します。
# 画像を呼び出すコード
先程の Cookpad のブログを参考に次のようなコードを書きます。
今回、登録した画像がsmile
とcirlce
の場合には次のようなコードになります。
import Foundation
import SwiftUI
// Enum名は自身が認識できれば何でも良い
public enum FontAwesome: String, CaseIterable {
public enum Package {
public static let namespace = "FA5" // Namespaceを指定
public static let version = "1.0.0"
}
// 登録した画像に対してEnumを割り当てる
case circle = "circle"
case smile = "smile"
public var imageName: String {
"\(Package.namespace)/\(rawValue)"
}
}
// 画像としてすぐに呼び出せるようにイニシャライザを設定
extension Image {
init(_ symbol: FontAwesome) {
self.init(symbol.imageName, bundle: .main)
}
}
こうしておけば、SwiftUI 側からImage(FontAwesome.smile)
として画像を読み込むことができます。Enum ですので一度登録してしまえば Typo の可能性はありませんし、これがとても便利だと思いました。
記事は以上。