#とりあえずおさらい
Cocoa Touch Frameworkで初めて開発した時にハマるあるあるなのですが、UIImageを取得する際には、
public init?(named name: String)
ではなく、
@available(iOS 8.0, *)
public init?(named name: String, in bundle: Bundle?, compatibleWith traitCollection: UITraitCollection?)
を使用しなければなりません。
前者の方はメインのBundleからUIImageを取得するメソッドなので、メインであるアプリ側のBundleからUIImageを取得しようとしてしまいます。
メインのBundleがフレームワークのBundleではないので、フレームワーク側で前者を使用した場合、正しい画像取得ができなくなってしまうためです。
画像だけでなく、xibなどのリソースファイルをフレームワークのプロジェクト上で取得する際には、後者のような通常のアプリのプロジェクトで使用しているメソッドにさらにBundleを引数で指定するメソッドで取得しなければいけなかったりするので注意が必要です。
#classでバンドルを取得する場合
フレームワーク上では、フレームワークのBundleを取得しなければならないことがわかったので、その取得方法について見ていきましょう。
classで取得する場合は以下のメソッドを利用します。
public init(for aClass: Swift.AnyClass)
このメソッドを使用して以下の処理を実行すれば、classが所属するBundleを取得することが可能です。
let bundle = Bundle(for: type(of: self))
#enum・structの場合
Swiftでenumやstructの機能がかなり強化されたので、フレームワークでも使いたいパターンもあります。
その際もenumの値によって画像なんかをBundleから取得したいとかってよくあると思います。
そんな時、先ほどのclassで使用した方法は、あくまで、classが所属するBundleを取得するのでコンパイルエラーを起こしてしまいます。
そういう場合は、下記のように仮のクラスを作成して、そこからBundleを取得するのがいいのではないかと思います。
enum hoge {
case fuga
case piyo
var image: UIImage? {
//仮のクラスを作ってそこからBundle取得する
class DummyClass {}
let bundle = Bundle(for: type(of: DummyClass()))
switch self {
case .fuga:
return UIImage(named: "fugaImage", in: bundle, compatibleWith: nil)
case .piyo:
return UIImage(named: "piyoImage", in: bundle, compatibleWith: nil)
}
}
}
public struct SampleStruct {
var image: UIImage? {
//仮のクラスを作ってそこからBundle取得する
class DummyClass {}
let bundle = Bundle(for: type(of: DummyClass()))
return UIImage(named: "structImage", in: bundle, compatibleWith: nil)
}
}
#extension作ったよ
さすがに毎回仮のクラスを作るのは面倒なのでextension作ったよ。
extension Bundle {
static var current: Bundle {
class DummyClass{}
return Bundle(for: type(of: DummyClass()))
}
}
let bundle = Bundle.current