Skip to content

Instantly share code, notes, and snippets.

@danwood
Last active July 3, 2024 17:18
Show Gist options
  • Save danwood/94b6cabc72f17f44222ebda1841b22de to your computer and use it in GitHub Desktop.
Save danwood/94b6cabc72f17f44222ebda1841b22de to your computer and use it in GitHub Desktop.
macOS/iOS class for rendering emoji as a bitmap
//
// EmojiRender.swift
// ResponsiveDesignExamples
//
// Created by Dan Wood on 7/1/24.
//
import SwiftUI
import CoreGraphics
// A cross-platform convenience initializer
#if os(iOS) || os(watchOS) || os(tvOS)
extension Image {
init(nsImage: UIImage) {
self.init(uiImage: nsImage)
}
}
#endif
extension String {
static let nativeEmojiSize: CGFloat = 160
// Different classes and methods for rendering emoji, and slightly differing fudging offsets!!!
#if os(iOS) || os(watchOS) || os(tvOS)
func render(size: CGFloat) -> UIImage {
let font = UIFont.systemFont(ofSize: size)
let cgSize = CGSize(width: size, height: size)
return UIGraphicsImageRenderer(size: cgSize).image { context in
self.draw(at: CGPoint(x: -5.5 * (size/Self.nativeEmojiSize), y: -16 * (size/Self.nativeEmojiSize)),
withAttributes: [.font: font])
}
}
#elseif os(macOS)
func render(size: CGFloat) -> NSImage {
let cgSize = CGSize(width: size, height: size)
return NSImage(size: cgSize, flipped: false) { _ in
let attributes = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: size)]
let attributedString = NSAttributedString(string: self, attributes: attributes)
attributedString.draw(at: CGPoint(x: -5.5 * (size/Self.nativeEmojiSize), y: -14 * (size/Self.nativeEmojiSize)))
return true
}
}
#endif
}
// Create an image from emoji string. Only renders the first character.
extension Image {
init(emoji emoji: String) {
let nsImage = emoji.render(size: String.nativeEmojiSize) // 160 is "native" emoji bitmap size
// extracting CGImage may result in nil — fallback is to use the NSImage/UIImage init so we can return non-optional Image
if let cgImage = nsImage.cgImage(forProposedRect: nil, context: nil, hints: nil) {
self.init(cgImage, scale: 1, label: Text(emoji)) // attach emoji string for accessibility
} else {
self.init(nsImage: nsImage) // fallback, no accessibilty attached
}
}
}
struct EmojiTestView: View {
var body: some View {
Image(emoji: "🔵")
.resizable()
.aspectRatio(1, contentMode: .fit)
.background(.yellow)
// For testing, be sure to use an emoji that goes all the way to the edge like the circles.
// This is easiest to verify that it's centered properly
}
}
#Preview {
EmojiTestView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment