🏠
【SwiftUI】Menuのコンテンツのアイコンに絵文字を使いたい
SwiftUIのMenu
のコンテンツのアイコンに絵文字を使いたいと思い、色々と試してみたので共有します。
環境
- Xcode 15.2
Menu
Menu
を使用すると簡単にメニュー画面を表示することが出来ます。
Menu {
Label("House", systemImage: "house")
} label: {
Text("メニュー")
}
また、Label
を使用することで、Menu上にテキストとアイコンを表示することが出来ます。
絵文字をアイコンとして表示させたい
絵文字をMenu
上のアイコンとして表示させる為に色々と試してみました。
HStack
Menu {
HStack {
Text("House")
Text("🏠")
}
} label: {
Text("メニュー")
}
二つのコンテンツとして表示されてしまいました。
Label(title:icon:)
カスタムタイトルとアイコンを指定できるInitialierで試してみると、
Menu {
Label(title: { Text("House") },
icon: { Text("🏠") })
} label: {
Text("メニュー")
}
絵文字を指定したアイコンは表示されませんでした。
絵文字を画像にする
Text
だとアイコンとして表示されないようなので、思い切って絵文字を画像に変換しました。
絵文字の判定をする
まずは文字列が絵文字かどうかを判定するエクステンションを作成しました
extension Character {
/// Characterが絵文字かどうか
var isEmoji: Bool {
guard let scalar = unicodeScalars.first else { return false }
return scalar.properties.isEmoji && (scalar.value > 0x238C || unicodeScalars.count > 1)
}
}
extension String {
/// 文字列に絵文字が含まれているか
func containsEmoji() -> Bool {
for character in self {
if character.isEmoji {
return true
}
}
return false
}
}
絵文字をUIImageに変換する
絵文字かどうかを判定して、その後受け取った文字列から画像を作成しています。
extension UIImage {
static func generated(fromEmoji emoji: String, fontSize: CGFloat) -> UIImage? {
guard emoji.count == 1,
emoji.containsEmoji() else {
print("The emoji must be a single-character emoji.")
return nil
}
let attributes: [NSAttributedString.Key : Any] = [.font: UIFont.systemFont(ofSize: fontSize)]
let size = emoji.size(withAttributes: attributes)
return UIGraphicsImageRenderer(size: size).image { _ in
(emoji as NSString).draw(in: CGRect(origin: .zero, size: size),
withAttributes: attributes)
}
}
}
結果
import SwiftUI
struct ContentView: View {
/// 絵文字画像
var emojiImage: UIImage {
return UIImage.generated(fromEmoji: "🏠", fontSize: 24) ?? UIImage()
}
var body: some View {
Menu {
Label(title: { Text("House") },
icon: { Image(uiImage: emojiImage) })
} label: {
Text("メニュー")
}
}
}
無事に表示されました🎉
絵文字を画像にする (iOS 16+)
上記だと少し回りくどい処理になってしまいましたが、anz様にコメントいただき、iOS 16から使用できるImageRenderer
を使用することでより簡潔に書くことが出来ることが分かりました。
var body: some View {
Menu {
Label(
title: { Text("House") },
icon: { Image(uiImage: ImageRenderer(content: Text("🏠")).uiImage!) }
)
} label: {
Text("メニュー")
}
}
こちらでも同様の結果が表示されます🎉
まとめ
Menu
のコンテンツのアイコンを絵文字にしたい場合は、絵文字から画像を作成して渡すのが良さそうです。
iOS 16+だと、ImageRenderer
を使用してSwiftUIを簡潔に画像化できるので簡潔に記述できる。
おわりに
もっと最適な方法がある場合やこんな回りくどい方法をしなくても実現できる等あれば教えていただけると嬉しいです🙏🧻
参考
Discussion
気になったのでゴニョゴニョやってみた感じ。下記でも同様な表示できました
同じことを
ImageRenderer
使って簡潔にやっただけなのだけれど。。ImageRenderer
使うので iOS 16+ になっちゃうのもつらみありがとうございます!🙏
完全にこっちの方が書けており、個人的にも好きです!
追記させていただきます!!